This document contains a comprehensive workflow for the course Introduction to R: first steps, statistics, graphics, offered in the context of the CTH Course Series on Statistics - 2024.

The content will follow closely the slides used during the course itself, with particular highlighting regarding the code and its output. This should allow you to replicate all the steps, constituting a starting base for extending the commands e.g. to your own datasets.

Table of contents

  • Step 0: First things first: know your tools, R & RStudio
  • Step 1: Basics of R, RStudio, help, packages
  • Step 2: Data in, data out
  • Step 3: Analyzing (tabular) data: describe, explore, transform, summarise data
  • Step 4: Plotting data
  • Bonus Step: reproducible reports, i.e. escaping “the R script”

Schedule (tentatively)

9:00-10:00 — Step 0 & Bonus: How to make the most of this 10:00-12:30 — Step 1, 2, 3 (with short breaks in between)
12:30-13:30 — Lunch break
13:30-15:30 — Step 3, 4, Q&As

Step 0: You, R and RStudio, knowing you and knowing your tools

Let’s get to know each other a bit…

0.1 What is R? Why should I use R?

Available at https://www.r-project.org/

  • free statistical environment for interactive use
  • intepreted, functional scripting/programming language - you type in, you see output
  • descends from the S language, written by statisticians for statisticians

What can you do with R?

  • Anything!
    • do calculations
    • write functions
    • analyse data. ALL the data. Well, almost. But really, almost.
    • apply advanced statistical techniques
    • do beautiful & publication-ready plots
    • develop interactive web-applications
    • presentations & documents (this one)

Why should I use R?

  • it works! And it is quite powerful
  • it is free, open-source, and available for all OS
  • you can really do whatever you might aim to do in terms of statistics
  • it offers awesome possibilities for (interactive) graphing
  • it has a wide, active and competent community (ok, communities: statistics, bioinformatics, machine learning, …)
  • it can be extended with packages. More power!
  • escape Point-and-Click-land, you work with syntax: you can use, re-use elements, validate & reproduce analysis

Why should I not use R?

  • you use it or lose it
  • the learning curve might be steep
  • can be frustrating if you have errors
  • help might be available, but it is very technical
  • many packages, a blessing and a curse: how many, how good

0.2 Let’s get started!

Get R - and RStudio

Alternatives:

0.3 First ride: look around you

Open up RStudio - You’ll have four panes

  • the Source for your scripts and documents (top-left, in the default layout)
  • your Environment/History (top-right),
  • your Files/Plots/Packages/Help/Viewer (bottom-right), and
  • the R Console (bottom-left).

Want to customize this?

Tools \(\rightarrow\) Global Options \(\rightarrow\) Pane Layout

Advantages of an IDE

  • all in one window!
  • keyboard shortcuts, autocompletion, highlighting \(\rightarrow\) type easier, do less errors

0.4 Folder structure

It is good practice to keep a set of related data, analyses, and text self-contained in a single folder, called the working directory.

Why?

  • Easier to have “self-contained” research units!
  • A project “does not interfere” with other projects
  • Gives a structure, easier to find things, use, reuse
  • Someone else (including future you) can understand what goes on

How?

RStudio projects!
Custom settings, per project.

Let’s create one live now - and have the workspace NOT saved

What is the best structure?

One, used consistently - not gonna touch on naming things as it can get hot quickly :)

With R…

dir.create()

file.edit()

Where am I doing things?

The working directory is the place from where R will be looking for and saving the files.

getwd()/setwd(), but not in your scripts (fails on others’ computers!)

0.5 Interacting with R

Instructions, commands.

Scripts, console - use the editor and have a complete record on what you did!

Shortcuts FTW!

Even better: Reproducible documents, with R Markdown

Nice resources on top: * RStudio cheatsheet about the RStudio IDE! * the internet/rstats community!

0.6 Seeking help

  • ?
  • ??
  • built-in RStudio help interface - and shortcuts!

0.6.1 Where to ask for help?

  • your neighbour - if COVID-conform, do interact within each other!
  • your colleagues
  • https://rdocumentation.org/ website
  • the web: google, StackOverflow

The main point: describe well your problem, “help others help you”

Others need to reproduce your error to help you better: saveRDS(), dput(), sessionInfo()

0.7 R packages

R packages…

  • are fundamental components of R ecosystem
  • extend base R functionality for a specific purpose
  • bundle new functions, data sets, and documentation
  • are contributed by independent developers
  • have dependency management

Repositories:

  • CRAN: Managed official package repository network
  • Bioconductor: curated bioinformatics packages (vignettes mandatory, integrated ecosystem!)
  • GitHub: un-managed, bleeding edge - but also excellent ones (“just not on CRAN”)

Our contributions so far:

  • flowcatchR https://bioconductor.org/packages/flowcatchR/
  • pcaExplorer https://bioconductor.org/packages/pcaExplorer/
  • ideal https://bioconductor.org/packages/ideal/
  • GeneTonic (https://bioconductor.org/packages/GeneTonic)
  • iSEE (https://bioconductor.org/packages/iSEE)
  • simrec (for simulating recurring events, https://cran.r-project.org/web/packages/simrec/)

0.7.1 How to use packages

  • Install: once (for every major R version)
  • Load: in each session
  • Use like any base R functionality

For Bioconductor packages…

install.packages("BiocManager")
library("BiocManager")
BiocManager::install()

Relevant commands:

  • install.packages("packagename") - check it online at CRAN!
  • installed.packages()
  • .libPaths()
  • update.packages()
  • library("packagename")
  • help(package="packagename"), data(), browseVignettes(), vignette(), citation("packagename")

Something you might have already done, if you worked on RNA-seq data:

BiocManager::install("SummarizedExperiment")
BiocManager::install("DESeq2")

0.8 How should I use this material?

Use this very same text document and expand this!

Why? Let’s have a look first into the Bonus Content, actually: we navigate to Section 5 and continue from there.

1 Step 1: Introduction to R

Here we will touch on the first commands in R, so that you can

  • Define the following terms as they relate to R: object, assign, call, function, arguments, options.
  • Assign values to objects in R.
  • Learn how to name objects
  • Use comments to inform script.
  • Solve simple arithmetic operations in R.
  • Call functions and use arguments to change their default options.
  • Inspect the content of vectors and manipulate their content.
  • Subset and extract values from vectors.
  • Analyze vectors with missing data.

1.1 R is a powerful calculator

… but not just that.

Type the following

2 + 2
# [1] 4
log(2)
# [1] 0.6931472
347 * 73841
# [1] 25622827
7 / 2
# [1] 3.5
7 %/% 2
# [1] 3
7 %% 2
# [1] 1

What did these commands do?

1.2 🎶 Help! 🎶

# this calls the help for a function to plot a histogram
?hist
# this is just the same
help(hist) ## what about ??
?apropos
apropos("row")
#  [1] ".row"                   ".rowMeans"              ".rowNamesDF<-"         
#  [4] ".rowSums"               "arrows"                 "browseEnv"             
#  [7] "browser"                "browserCondition"       "browserSetDebug"       
# [10] "browserText"            "browseURL"              "browseVignettes"       
# [13] "n2mfrow"                "nrow"                   "NROW"                  
# [16] "PlantGrowth"            "row"                    "row.names"             
# [19] "row.names.data.frame"   "row.names.default"      "row.names<-"           
# [22] "row.names<-.data.frame" "row.names<-.default"    "rowMeans"              
# [25] "rownames"               "rownames<-"             "rowsum"                
# [28] "rowsum.data.frame"      "rowsum.default"         "rowSums"               
# [31] "ToothGrowth"            "xpdrows.data.frame"
  • integrated help system, with executable examples
  • (for some packages) vignettes (typical problem, commands, and workflow)
  • CRAN Task Views: https://cran.r-project.org/web/views/
  • Books!
  • Courses!
  • Online: mailing lists, forums (StackOverflow, …), blogs, Twitter (#rstats)

1.3 Your starting vocabulary - a.k.a. Exercise Session 0

  • getwd() and setwd() - Tab is your friend
  • <-, =: the assignment operator
  • ls(), rm()
  • str()
  • example(), help()/?[function]
  • print()
  • q()/quit()
  • logical operators: TRUE,FALSE,!,==,!=,<,>,<=,>=,|,&,xor()
  • c()
  • data have help items too: e.g. cars

Find out what these do!

1.3.1 Exercise Session 0 - Solutions

Click on this to display the solution
?getwd
?setwd
?`<-`
help(ls)
help(rm)
?str
?example
help(help)
?print
help(quit)
?c
?cars

1.4 Make your life easier - Notes for your future self

  • add comments and document your own code
# This is a comment
  • write clean code - use spaces, indentation
  • use an editor with syntax highlighting/some form of autocompletion

Careful here:

  • R is case sensitive and has zero-tolerance with mis-spelled names
  • parenthesis: open and close them
  • special attention with missing values, factors VS strings: R is clever, but you might think differently
  • do not be stingy with parentheses - if this helps you
  • same goes with comments - your colleagues and your future self will thank you

1.5 Exercise session 1

First things first:

  • Grab some mini-postit (for those of you participating in person)!
  • Get ready to use the chat & the reactions

Then:

  • find out more about the iris dataset. What is it about at all? How many variables are included? How many observations?
  • replicate! find out a function that replicates elements of a vector to produce this
1 1 1 1

BONUS: … and this

1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

1.5.1 Exercise Session 1 - Solutions

Click on this to display the solution
rep(1, 4)
# [1] 1 1 1 1

rep(c(1, 2, 3, 4, 5), 3)
#  [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

1.6 Data types

R can recognize different general types of data

  • numbers (numeric)

  • character strings (text)

  • logical (e.g. class(TRUE))

  • factors (“integers with a set of labels”) - it is categorical data!

  • special ones: dates, time, …

When and where to use which?

1.7 I was curious about the participants

In a previous edition of a similar course, I asked the future participants:

  • How old are you?
  • What is your current academic level? (PI, Postdoc, PhD, master)
  • What is your current knowledge level of R? (pro-good-intermediate-poor-none)
  • What is your knowledge of programming languages in general?
  • What is your experience level with genomics and RNA-seq data?
  • How familiar are you with mogon and parallel computing? (I am a regular user/Once in a while i used it/I know it exists/I heard we had some servers around/Is this supposed to be in the cloud?!)
  • What are your expectations from the course?

2 Step 2: Data in, data out

2.1 Importing data in R

80-20? 90-10? Import, clean, prepare, transform your data

Sources:

  • Files, Clipboard, URL
  • Plain text file: Comma-separated, tab-delimited, …
  • R format file
  • SAS / Stata / SPSS file: package haven
  • Spreadsheet (Excel): package readxl - highly recommended!
  • Database: RSQLite, RPostgreSQL, RMySQL, …

2.2 The vocabulary of importing

… and exporting

  • read.table(),write.table() + read.csv|delim
  • the option stringsAsFactors=FALSE
  • load(),save()/readRDS(),saveRDS()
  • via haven : read_sas(),read_spss() /write_sas(),write_sav()
  • via readxl: read_excel()

Check out their documentation pages!

Other options: rio, RStudio GUI

2.3 Take a look at the data

Go to https://github.com/federicomarini/rbioc2016

\(\rightarrow\) inst/extdata

\(\rightarrow\) survey_responses.csv, in its raw format

You can load it directly like this

surveyrbioc <- read.csv("https://raw.githubusercontent.com/federicomarini/rbioc2016/master/inst/extdata/survey_responses.csv")

Or install the package and load it from there

library("devtools")
install_github("federicomarini/rbioc2016")
library("rbioc2016")
data(surveyrbioc)

2.4 Input data: Step by step, by hand?

Sometimes your data is either small and/or not in an Excel-like tabular format.

What to do? You combine the elements together!

Q1 <- c(28, 27, 33, 32, 29)
# should return this
Q1
# [1] 28 27 33 32 29

Q2 <- c("PhD student", "PhD student", "Postdoc", "PhD student", "PhD student")
Q2
# [1] "PhD student" "PhD student" "Postdoc"     "PhD student" "PhD student"
# ... and so on

2.5 Combine the variables to a matrix

We have seen c(). We also have

  • cbind
  • rbind
firstTwo <- cbind(Q1, Q2)
firstTwo
#      Q1   Q2           
# [1,] "28" "PhD student"
# [2,] "27" "PhD student"
# [3,] "33" "Postdoc"    
# [4,] "32" "PhD student"
# [5,] "29" "PhD student"
rbind(Q1, Q2)
#    [,1]          [,2]          [,3]      [,4]          [,5]         
# Q1 "28"          "27"          "33"      "32"          "29"         
# Q2 "PhD student" "PhD student" "Postdoc" "PhD student" "PhD student"

Is this what you wanted?

2.6 Applying the first functions

But first, what can you do on these objects?

sum(Q1)
# [1] 149
sum(Q2)
# Error in sum(Q2): invalid 'type' (character) of argument
summary(Q1)
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#    27.0    28.0    29.0    29.8    32.0    33.0
summary(Q2)
#    Length     Class      Mode 
#         5 character character
str(Q1)
#  num [1:5] 28 27 33 32 29
str(Q2)
#  chr [1:5] "PhD student" "PhD student" "Postdoc" "PhD student" "PhD student"
mean(Q1)
# [1] 29.8
dim(firstTwo)
# [1] 5 2
firstTwo[, 1]
# [1] "28" "27" "33" "32" "29"
mean(firstTwo[, 1]) # Why, damn, why? Meet coercion
# [1] NA
class(firstTwo)
# [1] "matrix" "array"

2.7 matrix, data.frame and list

  • a matrix can contain one type of data - if numeric, you unleash all the matrix algebra power!
  • a data.frame can store more types of data (one per column)
  • a list is like a big box where you can put anything - but this is not always what you want

What is best?

Let’s try with a list

Q3 <- c("intermediate", "poor", "good", "none", "intermediate")
mylist <- list(Q1, Q2, Q3)
mylist
# [[1]]
# [1] 28 27 33 32 29
# 
# [[2]]
# [1] "PhD student" "PhD student" "Postdoc"     "PhD student" "PhD student"
# 
# [[3]]
# [1] "intermediate" "poor"         "good"         "none"         "intermediate"
## access your elements with
mylist[[1]]
# [1] 28 27 33 32 29
mylist[[1]][2]
# [1] 27

How do we create a data.frame?

mydf <- data.frame(
  age = Q1,
  level = Q2,
  rexp = Q3
)
mydf
#   age       level         rexp
# 1  28 PhD student intermediate
# 2  27 PhD student         poor
# 3  33     Postdoc         good
# 4  32 PhD student         none
# 5  29 PhD student intermediate
class(mydf$age)
# [1] "numeric"

2.7.1 Exploring a data.frame

mydf$age # it's all about the money :)
# [1] 28 27 33 32 29
mydf[, 1]
# [1] 28 27 33 32 29
names(mydf)
# [1] "age"   "level" "rexp"
rownames(mydf)
# [1] "1" "2" "3" "4" "5"
dim(mydf)
# [1] 5 3
nrow(mydf)
# [1] 5
ncol(mydf)
# [1] 3
surveyrbioc <- read.csv("https://raw.githubusercontent.com/federicomarini/rbioc2016/master/inst/extdata/survey_responses.csv")
head(surveyrbioc)
#   Q1          Q2           Q3           Q4           Q5                                    Q6
# 1 28 PhD student intermediate intermediate         good                   I am a regular user
# 2 27 PhD student         poor intermediate         poor                      I know it exists
# 3 33     Postdoc         good         good         good                      I know it exists
# 4 32 PhD student         poor         poor         poor Is this supposed to be in the cloud?!
# 5 29 PhD student         none         poor         poor                      I know it exists
# 6 33     Postdoc intermediate intermediate intermediate             Once in a while i used it
#                                                                                                                                                                                                                                               Q7
# 1                                                                                                                                                                                                Learn Parallelization with R (and Bioconductor)
# 2                                                                                                                                                                                                                                           <NA>
# 3                                                                                                                                                                                  use R scripts in parallel context (ex: alignments in RNA-seq)
# 4                                                                Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
# 5                                                                                                                                                                                                 Possible I will use R for editing RNA-seq data
# 6 I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
tail(surveyrbioc)
#    Q1                  Q2           Q3           Q4           Q5
# 17 28 Master student/else intermediate intermediate intermediate
# 18 39             Postdoc         good intermediate         good
# 19 32 Master student/else         none         poor    genoWhat?
# 20 29         PhD student         poor         none         poor
# 21 31         PhD student         none         none         good
# 22 30         PhD student         none         none         good
#                                       Q6                                                         Q7
# 17                      I know it exists       better understanding of R and to extend my knowledge
# 18                   I am a regular user            Mainly interested in parallel computing options
# 19 Is this supposed to be in the cloud?!                                                       <NA>
# 20 Is this supposed to be in the cloud?!          To better understand R and perform basic analysis
# 21    I heard we had some servers around                        working with fastq files based on R
# 22 Is this supposed to be in the cloud?! I want to be able to analyze my sequence data on my own :P
names(surveyrbioc)
# [1] "Q1" "Q2" "Q3" "Q4" "Q5" "Q6" "Q7"
str(surveyrbioc)
# 'data.frame': 22 obs. of  7 variables:
#  $ Q1: int  28 27 33 32 29 33 40 23 27 23 ...
#  $ Q2: chr  "PhD student" "PhD student" "Postdoc" "PhD student" ...
#  $ Q3: chr  "intermediate" "poor" "good" "poor" ...
#  $ Q4: chr  "intermediate" "intermediate" "good" "poor" ...
#  $ Q5: chr  "good" "poor" "good" "poor" ...
#  $ Q6: chr  "I am a regular user" "I know it exists" "I know it exists" "Is this supposed to be in the cloud?!" ...
#  $ Q7: chr  "Learn Parallelization with R (and Bioconductor)" NA "use R scripts in parallel context (ex: alignments in RNA-seq)" "Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would"| __truncated__ ...
summary(surveyrbioc)
#        Q1             Q2                 Q3                 Q4                 Q5           
#  Min.   :23.00   Length:22          Length:22          Length:22          Length:22         
#  1st Qu.:27.25   Class :character   Class :character   Class :character   Class :character  
#  Median :29.50   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
#  Mean   :30.14                                                                              
#  3rd Qu.:32.75                                                                              
#  Max.   :40.00                                                                              
#       Q6                 Q7           
#  Length:22          Length:22         
#  Class :character   Class :character  
#  Mode  :character   Mode  :character  
#                                       
#                                       
# 
surveyrbioc[, ]
#    Q1                  Q2           Q3           Q4           Q5
# 1  28         PhD student intermediate intermediate         good
# 2  27         PhD student         poor intermediate         poor
# 3  33             Postdoc         good         good         good
# 4  32         PhD student         poor         poor         poor
# 5  29         PhD student         none         poor         poor
# 6  33             Postdoc intermediate intermediate intermediate
# 7  40             Postdoc         good         good intermediate
# 8  23 Master student/else         poor         poor         good
# 9  27         PhD student         none         poor intermediate
# 10 23 Master student/else         poor         poor         poor
# 11 35             Postdoc         poor         poor intermediate
# 12 34 Master student/else         poor          pro         poor
# 13 31             Postdoc         none         none intermediate
# 14 27         PhD student         none         poor         poor
# 15 24 Master student/else         poor intermediate intermediate
# 16 28         PhD student         none         poor         poor
# 17 28 Master student/else intermediate intermediate intermediate
# 18 39             Postdoc         good intermediate         good
# 19 32 Master student/else         none         poor    genoWhat?
# 20 29         PhD student         poor         none         poor
# 21 31         PhD student         none         none         good
# 22 30         PhD student         none         none         good
#                                       Q6
# 1                    I am a regular user
# 2                       I know it exists
# 3                       I know it exists
# 4  Is this supposed to be in the cloud?!
# 5                       I know it exists
# 6              Once in a while i used it
# 7                    I am a regular user
# 8                       I know it exists
# 9                       I know it exists
# 10 Is this supposed to be in the cloud?!
# 11                      I know it exists
# 12                      I know it exists
# 13 Is this supposed to be in the cloud?!
# 14                      I know it exists
# 15                      I know it exists
# 16                      I know it exists
# 17                      I know it exists
# 18                   I am a regular user
# 19 Is this supposed to be in the cloud?!
# 20 Is this supposed to be in the cloud?!
# 21    I heard we had some servers around
# 22 Is this supposed to be in the cloud?!
#                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          Q7
# 1                                                                                                                                                                                                                                                                                                                                                                                                                                           Learn Parallelization with R (and Bioconductor)
# 2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
# 3                                                                                                                                                                                                                                                                                                                                                                                                                             use R scripts in parallel context (ex: alignments in RNA-seq)
# 4                                                                                                                                                                                                                                                                                                           Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
# 5                                                                                                                                                                                                                                                                                                                                                                                                                                            Possible I will use R for editing RNA-seq data
# 6                                                                                                                                                                                                                                            I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
# 7                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
# 8                                                                                                                                                                                                                                                                                                                                                                                                                                                      Get to know R better, basic commands
# 9                                                                                                                                                                                                                                                                                                                                                                                                                             To look if I can process my sequencing data on my own using R
# 10 I would like to learn more about R, especially how to use it in biomedial research. Im in the 2nd Semester of the Rasterprogramm biomedicine, last Semester I had two weeks bioinformatics and we used to work with R for statistics/ChIP-Seq/microarray-data analysis, but the time was too short, to go deep into it, we just scratched the surface. So now I wish to learn some more, would be great, if I could work with the programme by myself, for example for the masterthesis.
# 11                                                                                                                                                                                                                                                                                                                                                                                                                                                                       learn more about R
# 12                                                                                                                                                                                                                                                                                                                                                                                               Brush up on some R knowledge and maybe get some different perspective on Genome processing
# 13                                                                                                                                                                                                                                                                                                                                                                                                      to get a good and understandable introduction into R programming and bioinformatics
# 14                                                                                                                                                                                                                                                                                                                                                                                                                                                              learning how to work with R
# 15                                                                                                                                                                                                                                                                                                                                                                                                                                                     To learn the basics of R programming
# 16                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
# 17                                                                                                                                                                                                                                                                                                                                                                                                                                     better understanding of R and to extend my knowledge
# 18                                                                                                                                                                                                                                                                                                                                                                                                                                          Mainly interested in parallel computing options
# 19                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
# 20                                                                                                                                                                                                                                                                                                                                                                                                                                        To better understand R and perform basic analysis
# 21                                                                                                                                                                                                                                                                                                                                                                                                                                                      working with fastq files based on R
# 22                                                                                                                                                                                                                                                                                                                                                                                                                               I want to be able to analyze my sequence data on my own :P

Don’t forget some nice built-in like the View() function, in RStudio - this is a nice alternative to “pure browsing in Excel”.

2.8 Exercise session 2

Using the surveyrbioc object:

  • Calculate the mean age of the participants
  • How many participants did actually take part to the survey?
  • How old was the oldest participant? (max can be your help)
  • transpose the survey data and assign it to another variable
  • Change the column names of this object and save this data set as a tab-separated ASCII file
  • BONUS: what was the youngest participant expecting?

2.8.1 Exercise Session 2 - Solutions

Click on this to display the solution
mean(surveyrbioc$Q1)
# [1] 30.13636

max(surveyrbioc$Q1)
# [1] 40

my_transposed_survey <- t(surveyrbioc)

surveyrbioc_mod <- surveyrbioc
colnames(surveyrbioc_mod) <- c("age", "level", "rlevel", "prog_level", "genomics_level", "parcomp_level", "expectation")

surveyrbioc_mod$expectation[which.min(surveyrbioc_mod$age)]
# [1] "Get to know R better, basic commands"

3 Step 3: Analyzing (tabular) data

Describe, explore, transform, summarise data

3.1 Exploring, subsetting, manipulating, analysing

  • dim(x) shows the dimensions of an object
  • str(x) provides an overview of the structure of an object and the elements it contains
  • sum(x), mean(x), sd(x) computes the sum, mean, or standard deviation of all the elements in x; median(x), quantile(x)
  • length(x) returns the number of elements in x (a vector)
  • sqrt(x), log(x) take the square root and the natural logarithm of a numeric - element or vector
  • hist(x, breaks=20, col="blue") plots a histogram of variable x with 20 bins colored blue
  • unique(x) returns the vector of unique elements in x
  • rm(x) removes the object x from the environment (rm(list=ls()) removes all objects)
  • sessionInfo() prints information about R session and versions of all attached packages
  • logical operators might often come handy!

3.2 Subsetting the data

This is the basic way it works

surveyrbioc[ROWS, COLUMNS]

You can subset with…

  • integers
  • blank spaces
  • names
  • logical vectors

Try to make a guess, given this vector.

vec <- c(6, 1, 3, 6, 10, 5)

What happens if you do this?

vec[2]
# [1] 1
vec[c(5, 6)]
# [1] 10  5
vec[-c(5, 6)]
# [1] 6 1 3 6
vec > 5
# [1]  TRUE FALSE FALSE  TRUE  TRUE FALSE
vec[vec > 5]
# [1]  6  6 10

What happens if you do this?

df <- data.frame(
  name = c("John", "Paul", "George", "Ringo"),
  birth = c(1940, 1942, 1943, 1940),
  instrument = c("guitar", "bass", "guitar", "drums")
)

df
#     name birth instrument
# 1   John  1940     guitar
# 2   Paul  1942       bass
# 3 George  1943     guitar
# 4  Ringo  1940      drums

df[c(2, 4), 3]
# [1] "bass"  "drums"
df[, 1]
# [1] "John"   "Paul"   "George" "Ringo"
df[, "instrument"]
# [1] "guitar" "bass"   "guitar" "drums"
df$instrument
# [1] "guitar" "bass"   "guitar" "drums"

Back to the survey

# I just want the age
surveyrbioc[, 1]
#  [1] 28 27 33 32 29 33 40 23 27 23 35 34 31 27 24 28 28 39 32 29 31 30
# or
surveyrbioc$Q1
#  [1] 28 27 33 32 29 33 40 23 27 23 35 34 31 27 24 28 28 39 32 29 31 30

# the first 4 columns
surveyrbioc[, c(1, 2, 3, 4)]
#    Q1                  Q2           Q3           Q4
# 1  28         PhD student intermediate intermediate
# 2  27         PhD student         poor intermediate
# 3  33             Postdoc         good         good
# 4  32         PhD student         poor         poor
# 5  29         PhD student         none         poor
# 6  33             Postdoc intermediate intermediate
# 7  40             Postdoc         good         good
# 8  23 Master student/else         poor         poor
# 9  27         PhD student         none         poor
# 10 23 Master student/else         poor         poor
# 11 35             Postdoc         poor         poor
# 12 34 Master student/else         poor          pro
# 13 31             Postdoc         none         none
# 14 27         PhD student         none         poor
# 15 24 Master student/else         poor intermediate
# 16 28         PhD student         none         poor
# 17 28 Master student/else intermediate intermediate
# 18 39             Postdoc         good intermediate
# 19 32 Master student/else         none         poor
# 20 29         PhD student         poor         none
# 21 31         PhD student         none         none
# 22 30         PhD student         none         none
surveyrbioc[, 1:4]
#    Q1                  Q2           Q3           Q4
# 1  28         PhD student intermediate intermediate
# 2  27         PhD student         poor intermediate
# 3  33             Postdoc         good         good
# 4  32         PhD student         poor         poor
# 5  29         PhD student         none         poor
# 6  33             Postdoc intermediate intermediate
# 7  40             Postdoc         good         good
# 8  23 Master student/else         poor         poor
# 9  27         PhD student         none         poor
# 10 23 Master student/else         poor         poor
# 11 35             Postdoc         poor         poor
# 12 34 Master student/else         poor          pro
# 13 31             Postdoc         none         none
# 14 27         PhD student         none         poor
# 15 24 Master student/else         poor intermediate
# 16 28         PhD student         none         poor
# 17 28 Master student/else intermediate intermediate
# 18 39             Postdoc         good intermediate
# 19 32 Master student/else         none         poor
# 20 29         PhD student         poor         none
# 21 31         PhD student         none         none
# 22 30         PhD student         none         none

# all but the last column
surveyrbioc[, -7]
#    Q1                  Q2           Q3           Q4           Q5
# 1  28         PhD student intermediate intermediate         good
# 2  27         PhD student         poor intermediate         poor
# 3  33             Postdoc         good         good         good
# 4  32         PhD student         poor         poor         poor
# 5  29         PhD student         none         poor         poor
# 6  33             Postdoc intermediate intermediate intermediate
# 7  40             Postdoc         good         good intermediate
# 8  23 Master student/else         poor         poor         good
# 9  27         PhD student         none         poor intermediate
# 10 23 Master student/else         poor         poor         poor
# 11 35             Postdoc         poor         poor intermediate
# 12 34 Master student/else         poor          pro         poor
# 13 31             Postdoc         none         none intermediate
# 14 27         PhD student         none         poor         poor
# 15 24 Master student/else         poor intermediate intermediate
# 16 28         PhD student         none         poor         poor
# 17 28 Master student/else intermediate intermediate intermediate
# 18 39             Postdoc         good intermediate         good
# 19 32 Master student/else         none         poor    genoWhat?
# 20 29         PhD student         poor         none         poor
# 21 31         PhD student         none         none         good
# 22 30         PhD student         none         none         good
#                                       Q6
# 1                    I am a regular user
# 2                       I know it exists
# 3                       I know it exists
# 4  Is this supposed to be in the cloud?!
# 5                       I know it exists
# 6              Once in a while i used it
# 7                    I am a regular user
# 8                       I know it exists
# 9                       I know it exists
# 10 Is this supposed to be in the cloud?!
# 11                      I know it exists
# 12                      I know it exists
# 13 Is this supposed to be in the cloud?!
# 14                      I know it exists
# 15                      I know it exists
# 16                      I know it exists
# 17                      I know it exists
# 18                   I am a regular user
# 19 Is this supposed to be in the cloud?!
# 20 Is this supposed to be in the cloud?!
# 21    I heard we had some servers around
# 22 Is this supposed to be in the cloud?!
# if you don't know we had 7 columns...
surveyrbioc[, -ncol(surveyrbioc)]
#    Q1                  Q2           Q3           Q4           Q5
# 1  28         PhD student intermediate intermediate         good
# 2  27         PhD student         poor intermediate         poor
# 3  33             Postdoc         good         good         good
# 4  32         PhD student         poor         poor         poor
# 5  29         PhD student         none         poor         poor
# 6  33             Postdoc intermediate intermediate intermediate
# 7  40             Postdoc         good         good intermediate
# 8  23 Master student/else         poor         poor         good
# 9  27         PhD student         none         poor intermediate
# 10 23 Master student/else         poor         poor         poor
# 11 35             Postdoc         poor         poor intermediate
# 12 34 Master student/else         poor          pro         poor
# 13 31             Postdoc         none         none intermediate
# 14 27         PhD student         none         poor         poor
# 15 24 Master student/else         poor intermediate intermediate
# 16 28         PhD student         none         poor         poor
# 17 28 Master student/else intermediate intermediate intermediate
# 18 39             Postdoc         good intermediate         good
# 19 32 Master student/else         none         poor    genoWhat?
# 20 29         PhD student         poor         none         poor
# 21 31         PhD student         none         none         good
# 22 30         PhD student         none         none         good
#                                       Q6
# 1                    I am a regular user
# 2                       I know it exists
# 3                       I know it exists
# 4  Is this supposed to be in the cloud?!
# 5                       I know it exists
# 6              Once in a while i used it
# 7                    I am a regular user
# 8                       I know it exists
# 9                       I know it exists
# 10 Is this supposed to be in the cloud?!
# 11                      I know it exists
# 12                      I know it exists
# 13 Is this supposed to be in the cloud?!
# 14                      I know it exists
# 15                      I know it exists
# 16                      I know it exists
# 17                      I know it exists
# 18                   I am a regular user
# 19 Is this supposed to be in the cloud?!
# 20 Is this supposed to be in the cloud?!
# 21    I heard we had some servers around
# 22 Is this supposed to be in the cloud?!

# you can subset with logical vectors, by row and by column
surveyrbioc[c(rep(TRUE, 10), rep(FALSE, 8)), ]
#    Q1                  Q2           Q3           Q4           Q5
# 1  28         PhD student intermediate intermediate         good
# 2  27         PhD student         poor intermediate         poor
# 3  33             Postdoc         good         good         good
# 4  32         PhD student         poor         poor         poor
# 5  29         PhD student         none         poor         poor
# 6  33             Postdoc intermediate intermediate intermediate
# 7  40             Postdoc         good         good intermediate
# 8  23 Master student/else         poor         poor         good
# 9  27         PhD student         none         poor intermediate
# 10 23 Master student/else         poor         poor         poor
# 19 32 Master student/else         none         poor    genoWhat?
# 20 29         PhD student         poor         none         poor
# 21 31         PhD student         none         none         good
# 22 30         PhD student         none         none         good
#                                       Q6
# 1                    I am a regular user
# 2                       I know it exists
# 3                       I know it exists
# 4  Is this supposed to be in the cloud?!
# 5                       I know it exists
# 6              Once in a while i used it
# 7                    I am a regular user
# 8                       I know it exists
# 9                       I know it exists
# 10 Is this supposed to be in the cloud?!
# 19 Is this supposed to be in the cloud?!
# 20 Is this supposed to be in the cloud?!
# 21    I heard we had some servers around
# 22 Is this supposed to be in the cloud?!
#                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          Q7
# 1                                                                                                                                                                                                                                                                                                                                                                                                                                           Learn Parallelization with R (and Bioconductor)
# 2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
# 3                                                                                                                                                                                                                                                                                                                                                                                                                             use R scripts in parallel context (ex: alignments in RNA-seq)
# 4                                                                                                                                                                                                                                                                                                           Id like to gather practise to analyse count data and develope the necessary data design. Additionally, it would be interesting to get to now how to find putative novel miRNAs.
# 5                                                                                                                                                                                                                                                                                                                                                                                                                                            Possible I will use R for editing RNA-seq data
# 6                                                                                                                                                                                                                                            I would describe myself as an advanced beginner, I am actively using R now to look (mostly) at count tables from NGS data and make plots. I am especially interested in the Bioconductor and parallelization in R section, this is new for me.
# 7                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      <NA>
# 8                                                                                                                                                                                                                                                                                                                                                                                                                                                      Get to know R better, basic commands
# 9                                                                                                                                                                                                                                                                                                                                                                                                                             To look if I can process my sequencing data on my own using R
# 10 I would like to learn more about R, especially how to use it in biomedial research. Im in the 2nd Semester of the Rasterprogramm biomedicine, last Semester I had two weeks bioinformatics and we used to work with R for statistics/ChIP-Seq/microarray-data analysis, but the time was too short, to go deep into it, we just scratched the surface. So now I wish to learn some more, would be great, if I could work with the programme by myself, for example for the masterthesis.
# 19                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     <NA>
# 20                                                                                                                                                                                                                                                                                                                                                                                                                                        To better understand R and perform basic analysis
# 21                                                                                                                                                                                                                                                                                                                                                                                                                                                      working with fastq files based on R
# 22                                                                                                                                                                                                                                                                                                                                                                                                                               I want to be able to analyze my sequence data on my own :P
surveyrbioc[c(TRUE, FALSE), ] # keep in mind this behavior!
#    Q1                  Q2           Q3           Q4           Q5
# 1  28         PhD student intermediate intermediate         good
# 3  33             Postdoc         good         good         good
# 5  29         PhD student         none         poor         poor
# 7  40             Postdoc         good         good intermediate
# 9  27         PhD student         none         poor intermediate
# 11 35             Postdoc         poor         poor intermediate
# 13 31             Postdoc         none         none intermediate
# 15 24 Master student/else         poor intermediate intermediate
# 17 28 Master student/else intermediate intermediate intermediate
# 19 32 Master student/else         none         poor    genoWhat?
# 21 31         PhD student         none         none         good
#                                       Q6
# 1                    I am a regular user
# 3                       I know it exists
# 5                       I know it exists
# 7                    I am a regular user
# 9                       I know it exists
# 11                      I know it exists
# 13 Is this supposed to be in the cloud?!
# 15                      I know it exists
# 17                      I know it exists
# 19 Is this supposed to be in the cloud?!
# 21    I heard we had some servers around
#                                                                                     Q7
# 1                                      Learn Parallelization with R (and Bioconductor)
# 3                        use R scripts in parallel context (ex: alignments in RNA-seq)
# 5                                       Possible I will use R for editing RNA-seq data
# 7                                                                                 <NA>
# 9                        To look if I can process my sequencing data on my own using R
# 11                                                                  learn more about R
# 13 to get a good and understandable introduction into R programming and bioinformatics
# 15                                                To learn the basics of R programming
# 17                                better understanding of R and to extend my knowledge
# 19                                                                                <NA>
# 21                                                 working with fastq files based on R

# guess what this does?
surveyrbioc$Q2 == "PhD student"
#  [1]  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE
# [17] FALSE FALSE FALSE  TRUE  TRUE  TRUE

3.3 Exercise session 3

  • How many PhD students did reply?
  • What was the proportion of PhD students to all other participants?
  • How old were they on average?
  • How many of the participants were older than 30?
  • How many postdocs were younger than 35?
  • How many of the participants did not reply to the last question?

3.3.1 Exercise Session 3 - Solutions

Click on this to display the solution
sum(surveyrbioc$Q2 == "PhD student")
# [1] 10
mean(surveyrbioc$Q2 == "PhD student")
# [1] 0.4545455
mean(surveyrbioc$Q1[surveyrbioc$Q2 == "PhD student"])
# [1] 28.8
sum(surveyrbioc$Q1 >= 30)
# [1] 11
sum(surveyrbioc$Q1 < 35 & surveyrbioc$Q2 == "Postdoc")
# [1] 3
sum(is.na(surveyrbioc$Q7))
# [1] 4

3.4 Manipulating and analysing your data

You can

  • sort the data (see sort and order)
  • transform your data: apply rules (formulas, logics, insight altogether)
  • combine two datasets or more (if you merge them)
  • do some statistics on your data

3.5 Sorting the data

myord <- order(surveyrbioc$Q1)
myord
#  [1]  8 10 15  2  9 14  1 16 17  5 20 22 13 21  4 19  3  6 12 11 18  7

head(surveyrbioc[myord, 1:5], 4)
#    Q1                  Q2   Q3           Q4           Q5
# 8  23 Master student/else poor         poor         good
# 10 23 Master student/else poor         poor         poor
# 15 24 Master student/else poor intermediate intermediate
# 2  27         PhD student poor intermediate         poor
sorted_surv <- surveyrbioc[myord, 1:6]

sort() returns you the sorted data, order() the indices only

3.6 Transforming the data

# transforming a variable
newsurvey <- surveyrbioc[, 1:5]
newsurvey$ageroot <- sqrt(newsurvey$Q1)
head(newsurvey)
#   Q1          Q2           Q3           Q4           Q5  ageroot
# 1 28 PhD student intermediate intermediate         good 5.291503
# 2 27 PhD student         poor intermediate         poor 5.196152
# 3 33     Postdoc         good         good         good 5.744563
# 4 32 PhD student         poor         poor         poor 5.656854
# 5 29 PhD student         none         poor         poor 5.385165
# 6 33     Postdoc intermediate intermediate intermediate 5.744563

# creating groups out of a continuous variable
newsurvey$agegroup <- cut(newsurvey$Q1, breaks = c(20, 30, 40))
head(newsurvey)
#   Q1          Q2           Q3           Q4           Q5  ageroot agegroup
# 1 28 PhD student intermediate intermediate         good 5.291503  (20,30]
# 2 27 PhD student         poor intermediate         poor 5.196152  (20,30]
# 3 33     Postdoc         good         good         good 5.744563  (30,40]
# 4 32 PhD student         poor         poor         poor 5.656854  (30,40]
# 5 29 PhD student         none         poor         poor 5.385165  (20,30]
# 6 33     Postdoc intermediate intermediate intermediate 5.744563  (30,40]

Use case for merge: you have two sets you are playing with! Think in advance what you need for that purpose…

3.7 We want statistics!

Are PhD students significantly younger than postdocs? Are there any differences in the age of the three groups?

phds <- surveyrbioc[surveyrbioc$Q2 == "PhD student", ]
postdocs <- surveyrbioc[surveyrbioc$Q2 == "Postdoc", ]
t.test(phds$Q1, postdocs$Q1)
# 
#   Welch Two Sample t-test
# 
# data:  phds$Q1 and postdocs$Q1
# t = -4.0528, df = 6.4476, p-value = 0.005767
# alternative hypothesis: true difference in means is not equal to 0
# 95 percent confidence interval:
#  -10.146796  -2.586537
# sample estimates:
# mean of x mean of y 
#  28.80000  35.16667
aov(data = surveyrbioc, Q1 ~ Q2) # What is missing here?
# Call:
#    aov(formula = Q1 ~ Q2, data = surveyrbioc)
# 
# Terms:
#                       Q2 Residuals
# Sum of Squares  216.8242  207.7667
# Deg. of Freedom        2        19
# 
# Residual standard error: 3.306824
# Estimated effects may be unbalanced

Much more on this: in the next courses!

3.8 Simple yet powerful functions

tapply

You want to calculate the median age of each academic group in here

md <- median(surveyrbioc$Q1)
md_master <- median(surveyrbioc$Q1[surveyrbioc$Q2 == "Master student/else"])
md_phd <- median(surveyrbioc$Q1[surveyrbioc$Q2 == "PhD student"])
md_postdocs <- median(surveyrbioc$Q1[surveyrbioc$Q2 == "Postdoc"])
c(md_master, md_phd, md_postdocs)
# [1] 26.0 28.5 34.0

tapply splits the data of the first variable on the levels of the second variable, and applies the function (any function)

tapply(X = surveyrbioc$Q1, INDEX = surveyrbioc$Q2, FUN = median)
# Master student/else         PhD student             Postdoc 
#                26.0                28.5                34.0

lapply and sapply

Back to our iris dataset

names(iris)
# [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"

We want the average sepal length and width, and the same for the petals. Uh, and we want the standard deviation too.

# the unefficient way:
seplen_m <- mean(iris$Sepal.Length)
sepwid_m <- mean(iris$Sepal.Width)
petlen_m <- mean(iris$Petal.Length)
petwid_m <- mean(iris$Petal.Width)

seplen_m <- sd(iris$Sepal.Length)
# ... and so on

\(\rightarrow\) Apply a Function over a List or Vector

# we will use just the first four columns
lapply(iris[, 1:4], mean)
# $Sepal.Length
# [1] 5.843333
# 
# $Sepal.Width
# [1] 3.057333
# 
# $Petal.Length
# [1] 3.758
# 
# $Petal.Width
# [1] 1.199333
sapply(iris[, 1:4], mean)
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#     5.843333     3.057333     3.758000     1.199333
lapply(iris[, 1:4], sd)
# $Sepal.Length
# [1] 0.8280661
# 
# $Sepal.Width
# [1] 0.4358663
# 
# $Petal.Length
# [1] 1.765298
# 
# $Petal.Width
# [1] 0.7622377
# ...

The major difference is in the presentation of the output

3.9 A very good friend: get a summary of your data

and some other friends…

Try out summary on a data.frame

summary(iris)
#   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
#  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
#  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
#  Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
#  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
#  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
#  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500

Alternatives in other packages:

  • describe() in the Hmisc package
  • skim() from skimr
  • create_report() from Data Explorer

table

table(surveyrbioc$Q3)
# 
#         good intermediate         none         poor 
#            3            3            8            8
table(surveyrbioc$Q4)
# 
#         good intermediate         none         poor          pro 
#            2            6            4            9            1

table(surveyrbioc$Q2, surveyrbioc$Q3)
#                      
#                       good intermediate none poor
#   Master student/else    0            1    1    4
#   PhD student            0            1    6    3
#   Postdoc                3            1    1    1
  • want the sums? Try addmargins()
  • looking for the percentage values? prop.table()
  • somewhat nicer output: ftable()
addmargins(table(surveyrbioc$Q2, surveyrbioc$Q3))
#                      
#                       good intermediate none poor Sum
#   Master student/else    0            1    1    4   6
#   PhD student            0            1    6    3  10
#   Postdoc                3            1    1    1   6
#   Sum                    3            3    8    8  22
prop.table(table(surveyrbioc$Q2, surveyrbioc$Q3))
#                      
#                             good intermediate       none       poor
#   Master student/else 0.00000000   0.04545455 0.04545455 0.18181818
#   PhD student         0.00000000   0.04545455 0.27272727 0.13636364
#   Postdoc             0.13636364   0.04545455 0.04545455 0.04545455

Please always do check the docs!

3.10 Exercise session 4

The MASS package contains the dataset Cars93, which stores the data on 93 makes of car sold in US

  • you’ll need the package and the data
  • Type specifies the type of market the car is aimed at. Find the cheapest car in each type, and the one with the greatest fuel efficiency
  • compute the mean horsepower for each type
  • create two data.frames, one for US cars, the other one with non-US cars
  • export the US cars to a text file
  • save the non-US cars data to a binary file (.RData)

3.10.1 Exercise Session 4 - Solutions

Click on this to display the solution
library(MASS)
head(Cars93)
#   Manufacturer   Model    Type Min.Price Price Max.Price MPG.city MPG.highway            AirBags
# 1        Acura Integra   Small      12.9  15.9      18.8       25          31               None
# 2        Acura  Legend Midsize      29.2  33.9      38.7       18          25 Driver & Passenger
# 3         Audi      90 Compact      25.9  29.1      32.3       20          26        Driver only
# 4         Audi     100 Midsize      30.8  37.7      44.6       19          26 Driver & Passenger
# 5          BMW    535i Midsize      23.7  30.0      36.2       22          30        Driver only
# 6        Buick Century Midsize      14.2  15.7      17.3       22          31        Driver only
#   DriveTrain Cylinders EngineSize Horsepower  RPM Rev.per.mile Man.trans.avail Fuel.tank.capacity
# 1      Front         4        1.8        140 6300         2890             Yes               13.2
# 2      Front         6        3.2        200 5500         2335             Yes               18.0
# 3      Front         6        2.8        172 5500         2280             Yes               16.9
# 4      Front         6        2.8        172 5500         2535             Yes               21.1
# 5       Rear         4        3.5        208 5700         2545             Yes               21.1
# 6      Front         4        2.2        110 5200         2565              No               16.4
#   Passengers Length Wheelbase Width Turn.circle Rear.seat.room Luggage.room Weight  Origin
# 1          5    177       102    68          37           26.5           11   2705 non-USA
# 2          5    195       115    71          38           30.0           15   3560 non-USA
# 3          5    180       102    67          37           28.0           14   3375 non-USA
# 4          6    193       106    70          37           31.0           17   3405 non-USA
# 5          4    186       109    69          39           27.0           13   3640 non-USA
# 6          6    189       105    69          41           28.0           16   2880     USA
#            Make
# 1 Acura Integra
# 2  Acura Legend
# 3       Audi 90
# 4      Audi 100
# 5      BMW 535i
# 6 Buick Century
?Cars93
tapply(X = Cars93$Min.Price, INDEX = Cars93$Type, FUN = min)
# Compact   Large Midsize   Small  Sporty     Van 
#     8.5    17.5    12.4     6.7     9.1    13.6
tapply(X = Cars93$Horsepower, INDEX = Cars93$Type, FUN = mean)
#  Compact    Large  Midsize    Small   Sporty      Van 
# 131.0000 179.4545 173.0909  91.0000 160.1429 149.4444

table(Cars93$Origin)
# 
#     USA non-USA 
#      48      45
us_cars <- Cars93[Cars93$Origin == "USA", ]
nonus_cars <- Cars93[Cars93$Origin != "USA", ]
# write.csv(us_cars, file = "us_cars.csv")
# save(nonus_cars, file = "nonus_cars.RData")

4 Step 4: Plotting data

4.1 Graphics in R

  • powerful environment for visualizing scientific data
  • integrated graphics AND statistics
  • publication-ready quality
  • fully programmable, highly reproducible

Many ways for the same task:

  • base graphics (plot)
  • ggplot2
  • lattice
  • interactive visualizations such as plotly, ggvis or other libraries

Why bother plotting at all?

  • facilitate comparisons
  • identify trends
  • generate hypotheses

4.2 The plot function

First thing: take a look at the overview documentation of plot

?plot
# Help on topic 'plot' was found in the following packages:
# 
#   Package               Library
#   base                  /Library/Frameworks/R.framework/Resources/library
#   graphics              /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/library
# 
# 
# Using the first match ...

We will see

  • scatter plots
  • boxplots
  • barplots
  • histograms

4.3 plot parameters

Required:

  • x variable
  • y variable

Other options

  • title with main
  • axes labels with xlab and ylab
  • axes limits with xlim and ylim
  • symbols, colors and sizes: pch, col and cex - as atomic elements or as vectors

4.4 Get to know the data: mpg

library(ggplot2) # this is useful per se, and contains the dataset we will be using
?mpg
This dataset contains a subset of the fuel economy data that the EPA makes available on http://fueleconomy.gov
# works on RStudio
# View(mpg)
# otherwise stick to the classic
str(mpg)
# tibble [234 × 11] (S3: tbl_df/tbl/data.frame)
#  $ manufacturer: chr [1:234] "audi" "audi" "audi" "audi" ...
#  $ model       : chr [1:234] "a4" "a4" "a4" "a4" ...
#  $ displ       : num [1:234] 1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
#  $ year        : int [1:234] 1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
#  $ cyl         : int [1:234] 4 4 4 4 6 6 6 4 4 4 ...
#  $ trans       : chr [1:234] "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
#  $ drv         : chr [1:234] "f" "f" "f" "f" ...
#  $ cty         : int [1:234] 18 21 20 21 16 18 18 18 16 20 ...
#  $ hwy         : int [1:234] 29 29 31 30 26 26 27 26 25 28 ...
#  $ fl          : chr [1:234] "p" "p" "p" "p" ...
#  $ class       : chr [1:234] "compact" "compact" "compact" "compact" ...

Make a guess: what do you expect to see between fuel consumption and engine size?

4.5 Scatter plots

plot(mpg$displ, mpg$cty)

Bonus: what is the correlation?

cor(mpg$displ, mpg$cty)
# [1] -0.798524
cor(mpg$displ, mpg$cty, method = "spearman")
# [1] -0.8809049

4.5.1 Can we do more?

mpg$mygroup <- as.numeric(factor(mpg$class))
plot(mpg$displ, mpg$cty,
  col = mpg$mygroup
)
legend("topright", legend = levels(factor(mpg$class)), col = levels(factor(mpg$mygroup)), pch = 1)

plot(mpg$displ, mpg$cty,
  pch = as.numeric(factor((mpg$class)))
)

This shows we have quite some overlap of points. What can we do?

Adding some jitter…

plot(
  x = mpg$displ + rnorm(nrow(mpg), mean = 0, sd = 0.01),
  y = mpg$cty + rnorm(rnorm(nrow(mpg), mean = 0, sd = 0.01)),
  col = mpg$mygroup,
  main = "now with jitter!"
)

Adding a smoothing line

Trying to see a pattern? Add a smoothing curve.

This one is wrong - missing the reordering of points

plot(mpg$displ, mpg$cty, col = mpg$mygroup)
myloess <- loess(cty ~ displ, data = mpg)
myfit <- fitted(myloess)
lines(mpg$displ, myfit)
legend("topright", legend = levels(factor(mpg$class)), col = levels(factor(mpg$mygroup)), pch = 1)

This one is correct!

plot(mpg$displ, mpg$cty, col = mpg$mygroup)
myloess <- loess(cty ~ displ, data = mpg)
myfit <- fitted(myloess)
myord <- order(mpg$displ)
lines(mpg$displ[myord], myfit[myord])
legend("topright", legend = levels(factor(mpg$class)), col = levels(factor(mpg$mygroup)), pch = 1)

lines can add (almost) anything (any line).

points works in a similar way to superimpose, well, points

4.6 Bar charts

?barplot
academia_levels <- table(surveyrbioc$Q2)
barplot(academia_levels)

4.7 Boxplots

How is the age distributed across academic levels? Check the help of boxplot

  • A formula is required!
  • Don’t worry, it’s nothing but your y~x variables - ok, it can get more complicated
boxplot(Q1 ~ Q2,
  data = surveyrbioc
)

Splitting on more factors

boxplot(Q1 ~ Q2 + Q3,
  data = surveyrbioc
)

Making it more readable…

boxplot(Q1 ~ Q2 + Q3,
  data = surveyrbioc,
  las = 2
)

Changing the parameters allows you to control many aspects on plot appearance par is your best friend - and enemy (see ?par)

par(mar = c(15, 3, 2, 2))
boxplot(Q1 ~ Q2 + Q3, data = surveyrbioc, las = 2)

par( ... ) has many arguments; here, the useful/most used ones

  • mar for handling the margins
  • cex, col, pch and co. are all parameters of par
  • las to change the style of the axis labels
  • mfrow to draw an array of figures

4.8 Histograms

hist(surveyrbioc$Q1, breaks = 8)

4.9 More histograms!

hist(mpg$cty, breaks = 10)

hist(mpg$cty, breaks = 10, col = "steelblue")

hist(mpg$cty, breaks = 10, col = "steelblue", border = "gray")

hist(mpg$cty, breaks = 10, col = "steelblue", border = "gray", main = "Distribution of miles/gallon consumption in city traffic")

4.10 How to do nice pie charts

DON’T.

If you really need to do it…

?pie
example(pie) # expecially the last one
# 
# pie> require(grDevices)
# 
# pie> pie(rep(1, 24), col = rainbow(24), radius = 0.9)

# 
# pie> pie.sales <- c(0.12, 0.3, 0.26, 0.16, 0.04, 0.12)
# 
# pie> names(pie.sales) <- c("Blueberry", "Cherry",
# pie+     "Apple", "Boston Cream", "Other", "Vanilla Cream")
# 
# pie> pie(pie.sales) # default colours

# 
# pie> pie(pie.sales, col = c("purple", "violetred1", "green3",
# pie+                        "cornsilk", "cyan", "white"))

# 
# pie> pie(pie.sales, col = gray(seq(0.4, 1.0, length.out = 6)))

# 
# pie> pie(pie.sales, density = 10, angle = 15 + 10 * 1:6)

# 
# pie> pie(pie.sales, clockwise = TRUE, main = "pie(*, clockwise = TRUE)")

# 
# pie> segments(0, 0, 0, 1, col = "red", lwd = 2)
# 
# pie> text(0, 1, "init.angle = 90", col = "red")
# 
# pie> n <- 200
# 
# pie> pie(rep(1, n), labels = "", col = rainbow(n), border = NA,
# pie+     main = "pie(*, labels=\"\", col=rainbow(n), border=NA,..")

# 
# pie> ## Another case showing pie() is rather fun than science:
# pie> ## (original by FinalBackwardsGlance on http://imgur.com/gallery/wWrpU4X)
# pie> pie(c(Sky = 78, "Sunny side of pyramid" = 17, "Shady side of pyramid" = 5),
# pie+     init.angle = 315, col = c("deepskyblue", "yellow", "yellow3"), border = FALSE)

pie(c(20, 80),
  init.angle = -40,
  col = c("white", "yellow"),
  label = c("no pacman", "pacman"),
  border = "lightgrey"
)

… or switch from pie to waffle (seriously)

4.11 How to do 3D exploded pie charts

DON’T. And this time I mean it

sadly enough there would be packages for this, too

4.12 Extra: dynamite plots

a.k.a. Why is this bad?

age_by_group <- tapply(surveyrbioc$Q1, surveyrbioc$Q2, mean)
sd_by_group <- tapply(surveyrbioc$Q1, surveyrbioc$Q2, sd)
mybar <- barplot(age_by_group, col = c("khaki", "salmon", "firebrick"), ylim = c(0, max(age_by_group) + 5))
# mybar, inspect it
arrows(mybar, age_by_group, mybar, (age_by_group + sd_by_group), length = 0.15, angle = 90)

Dynamite plots VS boxplots

boxplot(Q1 ~ Q2,
  data = surveyrbioc
)

Median VS distribution VS actual points… What do you really want to show?

4.13 What can you do more with your plot?

  • change the points type - see type in ?plot
  • use log scales - see log
  • annotate (some of) the points - with text
  • change font sizes, styles and so on
  • use special characters with expression
  • save the plot
    • use the point-and-click interface in RStudio
    • code it

4.13.1 Saving your plots

General code structure for this

opendevice()
...
code for the plot
...
closedevice()
pdf("myfilename.pdf")
# see also alternatives:
## png()
## jpeg()
plot(mpg$displ, mpg$cty,
  col = mpg$mygroup
)
dev.off()

4.14 Petals and sepals

4.15 Exercise session 5

Back to the iris. Three species are there. Explore the dataset in the following ways:

  • draw a histogram of the petal length. What do you see?

  • plot sepal length versus petal length. Add different colors to highlight the species

  • do the same for sepal width and sepal length, and this time use a different symbol for the species. Add a legend and a title if you want

  • (harder) calculate the mean values of each feature for each species, organizing it in a matrix where the rows are the species names. Generate a stacked bar plot with it, and another one where the bars are arranged horizontally

  • feel free to go back to the survey data and explore it further!

4.15.1 Exercise Session 5 - Solutions

Click on this to display the solution
hist(iris$Petal.Length)

plot(iris$Sepal.Length, iris$Petal.Length)

plot(iris$Sepal.Length, iris$Petal.Length, col = iris$Species)

plot(iris$Sepal.Width, iris$Sepal.Length, pch = as.numeric(factor(iris$Species)))
legend("topright", legend = levels(factor(iris$Species)), pch = unique(factor(iris$Species)))


sl_means <- tapply(iris$Sepal.Length, iris$Species, mean)
pl_means <- tapply(iris$Petal.Length, iris$Species, mean)
sw_means <- tapply(iris$Sepal.Width, iris$Species, mean)
pw_means <- tapply(iris$Petal.Width, iris$Species, mean)
mymat <- cbind(sl_means, pl_means, sw_means, pw_means)
barplot(mymat, legend.text = unique(iris$Species))

barplot(mymat, beside = TRUE, legend.text = unique(iris$Species))

4.16 Something cool to have an overview…

pairs(iris[, 1:4], col = iris$Species)

You can use the panels even more cleverly, check the help of pairs!

This is a collection on graphs in R - with the underlying code too.

http://shiny.stat.ubc.ca/r-graph-catalog/

4.17 The gapminder project

Sit and relax/enjoy…

https://www.youtube.com/watch?v=hVimVzgtD6w

4.18 Meet ggplot2

But first, meet the gapminder data

library(gapminder)
head(gapminder)
# # A tibble: 6 × 6
#   country     continent  year lifeExp      pop gdpPercap
#   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
# 1 Afghanistan Asia       1952    28.8  8425333      779.
# 2 Afghanistan Asia       1957    30.3  9240934      821.
# 3 Afghanistan Asia       1962    32.0 10267083      853.
# 4 Afghanistan Asia       1967    34.0 11537966      836.
# 5 Afghanistan Asia       1972    36.1 13079460      740.
# 6 Afghanistan Asia       1977    38.4 14880372      786.
head(country_colors)
#          Nigeria            Egypt         Ethiopia Congo, Dem. Rep.     South Africa 
#        "#7F3B08"        "#833D07"        "#873F07"        "#8B4107"        "#8F4407" 
#            Sudan 
#        "#934607"
head(continent_colors)
#    Africa  Americas      Asia    Europe   Oceania 
# "#7F3B08" "#A50026" "#40004B" "#276419" "#313695"

Variables:

  • country
  • continent
  • year
  • lifeExp, life expectancy at birth
  • pop, total population
  • gdpPercap, per-capita GDP

4.19 The ggplot2 philosophy

gg stands for the grammar of graphics

  • you provide the data
  • you map the data to aesthetics (shape, size, colour)
  • you add geoms to specify how you want to have the data plotted
  • you can have statistical transformations
  • facets allow you to do quick elegant multi plots

It can come across somewhat harder since

  • data need to be tidy - one observation per row
  • requires an extra step for abstraction

yet, it makes the whole process of “thinking data” more natural.

4.19.1 A quick dive into the many options

ggplot(gapminder, aes(x = gdpPercap, y = lifeExp))

ggplot(gapminder, aes(x = gdpPercap, y = lifeExp)) +
  geom_point()

We can store ggplot plot objects into a variable - and build upon that later

p <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp))
p + geom_point()

p + geom_point() + scale_x_log10()

p <- p + scale_x_log10()
p + geom_point(color = "blue")

p + geom_point(color = "steelblue", pch = 19, size = 8, alpha = 1 / 4)

p + geom_point(aes(color = continent))

p + geom_point(aes(col = continent), size = 4)

p + geom_point(aes(col = continent, size = pop))

p + geom_point(aes(col = continent, size = pop)) + geom_smooth()

niceone <- p + geom_point(aes(col = continent, size = pop)) +
  geom_smooth(aes(col = continent), se = FALSE)
niceone

p + geom_point(aes(col = continent, size = pop)) +
  geom_smooth(lwd = 2, se = FALSE, method = "lm", col = "red")

p + geom_point(aes(col = continent, size = pop)) +
  geom_smooth(aes(col = continent), lwd = 2, se = FALSE, method = "lm")

p + geom_point() + facet_wrap(~continent)

p + geom_point(aes(col = continent)) + facet_wrap(~continent)

p + geom_point(aes(col = continent)) + geom_smooth() + facet_wrap(~continent)

4.19.2 Saving the plots

Becomes de facto easier - works on “plot objects”.

ggsave(file = "myplot.png")

4.19.3 Line plots

ggplot(
  gapminder,
  aes(x = year, y = lifeExp, group = country, color = country)
) +
  geom_line(lwd = 1, show.legend = FALSE) +
  scale_color_manual(values = country_colors) +
  theme_bw() +
  theme(strip.text = element_text(size = rel(1.1)))

bp <- ggplot(
  gapminder,
  aes(x = year, y = lifeExp, group = country, color = country)
) +
  geom_line(lwd = 1, show.legend = FALSE) +
  facet_wrap(~continent) +
  scale_color_manual(values = country_colors) +
  theme(strip.text = element_text(size = rel(1.1)))
bp

One step away from making things interactive - more interesting? more accessible? more explorable?

plotly::ggplotly(bp)

4.19.4 Boxplots

# now it is a categorical x VS continuous y
p <- ggplot(gapminder, aes(x = continent, y = lifeExp))
p + geom_point()

p + geom_point(alpha = 1 / 4)

It is so easy to escape dynamite plots!

p + geom_jitter()

p + geom_jitter(aes(col = continent))

p + geom_boxplot()

p + geom_boxplot() + geom_jitter(alpha = 1 / 2)

4.19.5 Histograms

p <- ggplot(gapminder, aes(lifeExp))
p + geom_histogram()

p + geom_histogram(binwidth = 1)

Stacked histogram are much easier in this framework

p + geom_histogram(aes(color = continent))

p + geom_histogram(aes(fill = continent))

p + geom_histogram(aes(fill = continent), position = "identity")

… and so is the superimposing of more than one distribution

p + geom_histogram(aes(fill = continent), position = "identity", alpha = 0.4)

Similar to histogram, you can use also density plots

p + geom_density(aes(fill = continent), alpha = 1 / 4)

4.19.6 Themes: a quick way to put a new shirt on

niceone + theme_bw()

niceone + theme_void()

If you really really really have to…

library("ggthemes")
niceone + theme_excel() + scale_color_excel()

4.20 Exercise session 6 - Homework if you want

  • try to recreate the plots you did with base graphics, this time using ggplot2

  • pick a nice plot you would like to have in your next manuscript: can you think of what you need to do it? I am talking of

    • what data type?
    • what transformations?
    • what plot type/layer?
Click on this to display the solution

5 Bonus Step: reproducible reports

Our aims:

  • Understand what R Markdown is and why you should use it
  • Learn how to construct an R Markdown file
  • Export an R Markdown file into many file formats
  • \(\rightarrow\) You are all set to use Rmd to document any of your analyses!

5.1 Reproducible reports with R Markdown

R Markdown allows you to create documents that serve as a neat record of your analysis.

Why?

  • we want other researchers to easily understand what we did in our analysis, otherwise nobody can be certain that you analysed your data properly (yay, reproducible research!)
  • create an R markdown document as an appendix to a paper or project assignment, upload it to an online repository such as Github, or simply to keep as a personal record (future you will thank present you for this)

The key point is…

R Markdown documents present your code alongside its output (graphs, tables, etc.) with conventional text to explain it, a bit like a notebook. To do this, R Markdown uses markdown syntax.

5.2 Markdown

Markdown is a very simple markup language which provides methods for creating documents with headers, images, links etc. from plain text files, while keeping the original plain text file easy to read.

You can convert Markdown documents to many other file types like .html or .pdf to display the headers, images etc..

It might sound complicated. But really isn’t,

First things first: install the required software

  • R and RStudio (guess you have it already)
install.packages("rmarkdown")
library(rmarkdown)
  • knitr comes along, pandoc too. You should quickly be all set!

5.3 Basics of markdown

ABC here, let’s go through it:

http://rmarkdown.rstudio.com/authoring_basics.html

plus… a beautiful cheat sheet is there for you!

http://rmarkdown.rstudio.com/lesson-15.html

http://www.rstudio.com/wp-content/uploads/2016/03/rmarkdown-cheatsheet-2.0.pdf

Using LaTeX? No problem, you can use \(\LaTeX\) here as well!

\(\left( f(x) = \sum_{i=0}^{n} \frac{a_i}{1+x} \right)\)

What can you do with R markdown?

http://rmarkdown.rstudio.com/gallery.html

http://rmarkdown.rstudio.com/formats.html

5.4 Let’s create one together!

5.4.1 Why is Rmd better than R

The price to pay to have an Rmd document is sooooo small - and for that, you get

  • code, text, output all together
  • one file only - no need to get lost
  • it even looks nice :)

5.4.2 Create an R Markdown file

To create a new R Markdown file (.Rmd), select File -> New File -> R Markdown... in RStudio, then choose the file type you want to create.

The newly created .Rmd file comes with basic instructions but we want to create our own R Markdown script, so let’s get to know the different parts of an Rmd file

  • An (optional) YAML header surrounded by ---s
  • R code chunks surrounded by backticks (```)
  • text mixed with simple text formatting

5.4.3 Inserting figures

Uh, you can insert figures also like this

![](images/grcat.png)

5.5 Insert text and code - any text, any code


```r
n <- 10
rnorm(n)
#  [1] -0.14014756  0.80533395 -0.52655265 -0.20807110  0.97189242  1.69874146 -0.99006732  0.39609724
#  [9]  0.90447946  0.04883601
```

Shortcut: Ctrl + Alt + I

Input code: you can use multiple languages including R, Python, and SQL, many more (specify the language in the chunk options)

Inline code can be added with `r 1+1`

5.5.1 Chunk options

Deatiled very nicely here: https://yihui.name/knitr/options/

A simple set of options which you can use for many documents:

set.seed(42)
knitr::opts_chunk$set(
  comment = NA,
  fig.align = "center",
  fig.width = 7,
  fig.height = 7,
  warning = FALSE,
  eval = TRUE
)

5.5.2 Knit!

Use the Knit button in the RStudio IDE to render the file and preview the output with a single click or keyboard shortcut (Ctrl + Shift + K).

To generate a report from the file, run the render command (works also outside of RStudio):

library("rmarkdown")
rmarkdown::render("yourfile.Rmd")

It was a deep dive, but now…

  • You are familiar with the Markdown syntax and code chunk rules.
  • You can include figures and tables in your Markdown reports.
  • You can create R Markdown files and export them to pdf or html files.

5.7 Exercise session Bonus

  • create a new R Markdown document
  • can you find out how to generate a word document as output?
  • insert some code you previously used for exploring the small survey data - remember, a fresh session is run when knitting, so you need the commands from the very start!

5.7.1 Exercise Session Bonus - Solutions

Click on this to display the solution
  • File -> New File -> R Markdown... in RStudio
  • add this in the yaml header
output:
  word_document

Useful material

Made @IMBEI

Books

Courses

Misc

Session Info

As a lesson, do take note/report the versions of R/the different packages you used for your analysis.

Why?

Click on this to display the solution
  • You will (should) report these in your Material & Methods
  • You credit the work of others - as a direct consequence
  • Different package versions might lead to different results
  • It is a good scientific research practice!
  • It helps others help you - in case you want to post your question on StackOverflow or similar
  • Because one command just does it for you
sessionInfo()
# R version 4.3.0 (2023-04-21)
# Platform: x86_64-apple-darwin20 (64-bit)
# Running under: macOS Monterey 12.7.1
# 
# Matrix products: default
# BLAS:   /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRblas.0.dylib 
# LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
# 
# locale:
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
# 
# time zone: Europe/Berlin
# tzcode source: internal
# 
# attached base packages:
# [1] stats     graphics  grDevices utils     datasets  methods   base     
# 
# other attached packages:
# [1] rmarkdown_2.25  ggthemes_5.0.0  gapminder_1.0.0 ggplot2_3.4.4   MASS_7.3-60.0.1
# 
# loaded via a namespace (and not attached):
#  [1] tidyr_1.3.1       plotly_4.10.4     rappdirs_0.3.3    sass_0.4.8        utf8_1.2.4       
#  [6] generics_0.1.3    icons_0.2.0       xml2_1.3.6        lattice_0.22-5    stringi_1.8.3    
# [11] digest_0.6.34     magrittr_2.0.3    evaluate_0.23     grid_4.3.0        timechange_0.3.0 
# [16] bookdown_0.37     fastmap_1.1.1     Matrix_1.6-5      jsonlite_1.8.8    formatR_1.14     
# [21] httr_1.4.7        mgcv_1.9-1        purrr_1.0.2       fansi_1.0.6       crosstalk_1.2.1  
# [26] viridisLite_0.4.2 scales_1.3.0      lazyeval_0.2.2    jquerylib_0.1.4   cli_3.6.2        
# [31] rlang_1.1.3       crayon_1.5.2      ellipsis_0.3.2    splines_4.3.0     munsell_0.5.0    
# [36] withr_3.0.0       cachem_1.0.8      yaml_2.3.8        tools_4.3.0       dplyr_1.1.4      
# [41] colorspace_2.1-0  assertthat_0.2.1  vctrs_0.6.5       R6_2.5.1          lifecycle_1.0.4  
# [46] lubridate_1.9.3   stringr_1.5.1     htmlwidgets_1.6.4 pkgconfig_2.0.3   bslib_0.6.1      
# [51] pillar_1.9.0      gtable_0.3.4      data.table_1.15.0 glue_1.7.0        xfun_0.41        
# [56] tibble_3.2.1      tidyselect_1.2.0  highr_0.10        emo_0.0.0.9000    rstudioapi_0.15.0
# [61] knitr_1.45        farver_2.1.1      nlme_3.1-164      htmltools_0.5.7   labeling_0.4.3   
# [66] compiler_4.3.0
LS0tCnRpdGxlOiA+CiAgSW50cm9kdWN0aW9uIHRvIFI6IGZpcnN0IHN0ZXBzLCBzdGF0aXN0aWNzLCBncmFwaGljcwpzdWJ0aXRsZTogPgogIENUSCBDb3Vyc2UgU2VyaWVzIG9uIFN0YXRpc3RpY3MgLSAyMDI0CiAgPHAgYWxpZ249ImNlbnRlciI+CiAgPGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS8iPgogIElNQkVJIEAgPGltZyBzcmM9ImltYWdlcy9jb21iaWxvZ28ucG5nIiBhbHQ9IiIgaGVpZ2h0PSI3MCIvPgogIDwhLS0gPGltZyBzcmM9ImltYWdlcy90aWNhcmRpby1sb2dvLnBuZyIgYWx0PSIiIGhlaWdodD0iNTAiLz4gLS0+CiAgPC9hPgogIDwvcD4KICA8YnI+CmF1dGhvcjoKLSBuYW1lOiBOYWpsYSBBYmFzc2kgKGFiYXNzaW5hQHVuaS1tYWluei5kZSk8YnI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS8iPklNQkVJLCBVbml2ZXJzaXR5IE1lZGljYWwgQ2VudGVyIE1haW56PC9hPjxicj4KLSBuYW1lOiBBbm5la2F0aHJpbiBOZWR3ZWQgKGFubmVsdWR0QHVuaS1tYWluei5kZSk8YnI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS8iPklNQkVJLCBVbml2ZXJzaXR5IE1lZGljYWwgQ2VudGVyIE1haW56PC9hPjxicj4KLSBuYW1lOiA8YSBocmVmPSJodHRwczovL2ZlZGVyaWNvbWFyaW5pLmdpdGh1Yi5pbyI+RmVkZXJpY28gTWFyaW5pIChtYXJpbmlmQHVuaS1tYWluei5kZSk8L2E+PGJyPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnVuaW1lZGl6aW4tbWFpbnouZGUvaW1iZWkvIj5JTUJFSSwgVW5pdmVyc2l0eSBNZWRpY2FsIENlbnRlciBNYWluejwvYT48YnI+PGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9GZWRlQmlvaW5mbyI+YHIgaWNvbnM6OmZvbnRhd2Vzb21lKCd0d2l0dGVyJylgIGBARmVkZUJpb2luZm9gPC9hPgpkYXRlOiAiMjAyNC8wMi8yMSIKb3V0cHV0OiAKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29sbGFwc2UgPSBUUlVFLAogIGNvbW1lbnQgPSAiIyIsCiAgZXJyb3IgPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFCikKb3B0aW9ucyh3aWR0aCA9IDEwMCkKYGBgCgpUaGlzIGRvY3VtZW50IGNvbnRhaW5zIGEgY29tcHJlaGVuc2l2ZSB3b3JrZmxvdyBmb3IgdGhlIGNvdXJzZSAqKkludHJvZHVjdGlvbiB0byBSOiBmaXJzdCBzdGVwcywgc3RhdGlzdGljcywgZ3JhcGhpY3MqKiwgb2ZmZXJlZCBpbiB0aGUgY29udGV4dCBvZiB0aGUgQ1RIIENvdXJzZSBTZXJpZXMgb24gU3RhdGlzdGljcyAtIDIwMjQuCgpUaGUgY29udGVudCB3aWxsIGZvbGxvdyBjbG9zZWx5IHRoZSBzbGlkZXMgdXNlZCBkdXJpbmcgdGhlIGNvdXJzZSBpdHNlbGYsIHdpdGggcGFydGljdWxhciBoaWdobGlnaHRpbmcgcmVnYXJkaW5nIHRoZSBjb2RlIGFuZCBpdHMgb3V0cHV0LiBUaGlzIHNob3VsZCBhbGxvdyB5b3UgdG8gcmVwbGljYXRlIGFsbCB0aGUgc3RlcHMsIGNvbnN0aXR1dGluZyBhIHN0YXJ0aW5nIGJhc2UgZm9yIGV4dGVuZGluZyB0aGUgY29tbWFuZHMgZS5nLiB0byB5b3VyIG93biBkYXRhc2V0cy4KCgojIFRhYmxlIG9mIGNvbnRlbnRzIHsudW5udW1iZXJlZH0KCi0gU3RlcCAwOiBGaXJzdCB0aGluZ3MgZmlyc3Q6IGtub3cgeW91ciB0b29scywgUiAmIFJTdHVkaW8KLSBTdGVwIDE6IEJhc2ljcyBvZiBSLCBSU3R1ZGlvLCBoZWxwLCBwYWNrYWdlcwotIFN0ZXAgMjogRGF0YSBpbiwgZGF0YSBvdXQKLSBTdGVwIDM6IEFuYWx5emluZyAodGFidWxhcikgZGF0YTogZGVzY3JpYmUsIGV4cGxvcmUsIHRyYW5zZm9ybSwgc3VtbWFyaXNlIGRhdGEKLSBTdGVwIDQ6IFBsb3R0aW5nIGRhdGEKLSBCb251cyBTdGVwOiByZXByb2R1Y2libGUgcmVwb3J0cywgaS5lLiBlc2NhcGluZyAidGhlIFIgc2NyaXB0IgoKKipTY2hlZHVsZSAodGVudGF0aXZlbHkpKioKCjk6MDAtMTA6MDAgLS0tIFN0ZXAgMCAmIEJvbnVzOiBIb3cgdG8gbWFrZSB0aGUgbW9zdCBvZiB0aGlzCjEwOjAwLTEyOjMwIC0tLSBTdGVwIDEsIDIsIDMgKHdpdGggc2hvcnQgYnJlYWtzIGluIGJldHdlZW4pICAKMTI6MzAtMTM6MzAgLS0tICpMdW5jaCBicmVhayogIAoxMzozMC0xNTozMCAtLS0gU3RlcCAzLCA0LCBRJkFzICAKCgojIFN0ZXAgMDogWW91LCBSIGFuZCBSU3R1ZGlvLCBrbm93aW5nIHlvdSBhbmQga25vd2luZyB5b3VyIHRvb2xzIHsjc3RlcDAgLnVubnVtYmVyZWR9CgpMZXQncyBnZXQgdG8ga25vdyBlYWNoIG90aGVyIGEgYml0Li4uCgojIyBXaGF0IGlzIFI/IFdoeSBzaG91bGQgSSB1c2UgUj8gCgpBdmFpbGFibGUgYXQgYGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvYAoKLSAqZnJlZSogc3RhdGlzdGljYWwgZW52aXJvbm1lbnQgZm9yIGludGVyYWN0aXZlIHVzZQotIGludGVwcmV0ZWQsIGZ1bmN0aW9uYWwgc2NyaXB0aW5nL3Byb2dyYW1taW5nIGxhbmd1YWdlIC0geW91IHR5cGUgaW4sIHlvdSBzZWUgb3V0cHV0Ci0gZGVzY2VuZHMgZnJvbSB0aGUgUyBsYW5ndWFnZSwgd3JpdHRlbiBieSBzdGF0aXN0aWNpYW5zIGZvciBzdGF0aXN0aWNpYW5zCiAgCldoYXQgY2FuIHlvdSBkbyB3aXRoIFI/CgotIEFueXRoaW5nIQogIC0gZG8gY2FsY3VsYXRpb25zCiAgLSB3cml0ZSBmdW5jdGlvbnMKICAtIGFuYWx5c2UgZGF0YS4gQUxMIHRoZSBkYXRhLiBXZWxsLCBhbG1vc3QuIEJ1dCByZWFsbHksIGFsbW9zdC4KICAtIGFwcGx5IGFkdmFuY2VkIHN0YXRpc3RpY2FsIHRlY2huaXF1ZXMKICAtIGRvIGJlYXV0aWZ1bCAmIHB1YmxpY2F0aW9uLXJlYWR5IHBsb3RzCiAgLSBkZXZlbG9wIGludGVyYWN0aXZlIHdlYi1hcHBsaWNhdGlvbnMKICAtIHByZXNlbnRhdGlvbnMgJiBkb2N1bWVudHMgKHRoaXMgb25lKQoKV2h5IHNob3VsZCBJIHVzZSBSPwoKLSBpdCB3b3JrcyEgQW5kIGl0IGlzIHF1aXRlIHBvd2VyZnVsCi0gaXQgaXMgZnJlZSwgb3Blbi1zb3VyY2UsIGFuZCBhdmFpbGFibGUgZm9yIGFsbCBPUwotIHlvdSBjYW4gKnJlYWxseSogZG8gd2hhdGV2ZXIgeW91IG1pZ2h0IGFpbSB0byBkbyBpbiB0ZXJtcyBvZiBzdGF0aXN0aWNzCi0gaXQgb2ZmZXJzIGF3ZXNvbWUgcG9zc2liaWxpdGllcyBmb3IgKGludGVyYWN0aXZlKSBncmFwaGluZwotIGl0IGhhcyBhIHdpZGUsIGFjdGl2ZSBhbmQgY29tcGV0ZW50IGNvbW11bml0eSAob2ssIGNvbW11bml0aWVzOiBzdGF0aXN0aWNzLCBiaW9pbmZvcm1hdGljcywgbWFjaGluZSBsZWFybmluZywgLi4uKQotIGl0IGNhbiBiZSBleHRlbmRlZCB3aXRoIHBhY2thZ2VzLiBNb3JlIHBvd2VyIQotIGVzY2FwZSBQb2ludC1hbmQtQ2xpY2stbGFuZCwgeW91IHdvcmsgd2l0aCBzeW50YXg6IHlvdSBjYW4gdXNlLCByZS11c2UgZWxlbWVudHMsIHZhbGlkYXRlICYgcmVwcm9kdWNlIGFuYWx5c2lzCgpXaHkgc2hvdWxkIEkgKm5vdCogdXNlIFI/CgotIHlvdSB1c2UgaXQgb3IgbG9zZSBpdAotIHRoZSBsZWFybmluZyBjdXJ2ZSBtaWdodCBiZSBzdGVlcAotIGNhbiBiZSBmcnVzdHJhdGluZyBpZiB5b3UgaGF2ZSBlcnJvcnMKLSBoZWxwIG1pZ2h0IGJlIGF2YWlsYWJsZSwgYnV0IGl0IGlzIHZlcnkgdGVjaG5pY2FsCi0gbWFueSBwYWNrYWdlcywgYSBibGVzc2luZyBhbmQgYSBjdXJzZTogaG93IG1hbnksIGhvdyBnb29kIAoKIyMgTGV0J3MgZ2V0IHN0YXJ0ZWQhCgpHZXQgUiAtIGFuZCBSU3R1ZGlvCgotIGdvIHRvIGh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyBhbmQgZ2V0IHRoZSBsYXRlc3QgdmVyc2lvbgotIGdvIHRvIGh0dHBzOi8vcG9zaXQuY28vcHJvZHVjdHMvb3Blbi1zb3VyY2UvcnN0dWRpby8gYW5kIGRvd25sb2FkIFJTdHVkaW8KLSAuLi4gb3IgdXNlIHlvdXIgdGV4dCBlZGl0b3Igb2YgY2hvaWNlCgpBbHRlcm5hdGl2ZXM6IAoKLSBPcGVuQW5hbHl0aWNzIEFyY2hpdGVjdCAoaHR0cHM6Ly93d3cuZ2V0YXJjaGl0ZWN0LmlvLykKLSBWaXN1YWwgU3R1ZGlvIENvZGUgKGh0dHBzOi8vY29kZS52aXN1YWxzdHVkaW8uY29tLykKLSBFbWFjcyBTcGVha3MgU3RhdGlzdGljcy4uLgoKCiMjIEZpcnN0IHJpZGU6IGxvb2sgYXJvdW5kIHlvdQoKT3BlbiB1cCBSU3R1ZGlvIC0gWW91J2xsIGhhdmUgZm91ciBwYW5lcwoKLSB0aGUgU291cmNlIGZvciB5b3VyIHNjcmlwdHMgYW5kIGRvY3VtZW50cyAodG9wLWxlZnQsIGluIHRoZSBkZWZhdWx0IGxheW91dCkKLSB5b3VyIEVudmlyb25tZW50L0hpc3RvcnkgKHRvcC1yaWdodCksCi0geW91ciBGaWxlcy9QbG90cy9QYWNrYWdlcy9IZWxwL1ZpZXdlciAoYm90dG9tLXJpZ2h0KSwgYW5kCi0gdGhlIFIgQ29uc29sZSAoYm90dG9tLWxlZnQpLgoKV2FudCB0byBjdXN0b21pemUgdGhpcz8KClRvb2xzICRccmlnaHRhcnJvdyQgR2xvYmFsIE9wdGlvbnMgJFxyaWdodGFycm93JCBQYW5lIExheW91dAoKQWR2YW50YWdlcyBvZiBhbiBJREUKCi0gYWxsIGluIG9uZSB3aW5kb3chCi0ga2V5Ym9hcmQgc2hvcnRjdXRzLCBhdXRvY29tcGxldGlvbiwgaGlnaGxpZ2h0aW5nICRccmlnaHRhcnJvdyQgdHlwZSBlYXNpZXIsIGRvIGxlc3MgZXJyb3JzCgoKIyMgRm9sZGVyIHN0cnVjdHVyZQoKSXQgaXMgZ29vZCBwcmFjdGljZSB0byBrZWVwIGEgc2V0IG9mIHJlbGF0ZWQgZGF0YSwgYW5hbHlzZXMsIGFuZCB0ZXh0IHNlbGYtY29udGFpbmVkIGluIGEgc2luZ2xlIGZvbGRlciwgY2FsbGVkIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4KCipXaHk/KgoKKiBFYXNpZXIgdG8gaGF2ZSAic2VsZi1jb250YWluZWQiIHJlc2VhcmNoIHVuaXRzIQoqIEEgcHJvamVjdCAiZG9lcyBub3QgaW50ZXJmZXJlIiB3aXRoIG90aGVyIHByb2plY3RzCiogR2l2ZXMgYSBzdHJ1Y3R1cmUsIGVhc2llciB0byBmaW5kIHRoaW5ncywgdXNlLCByZXVzZQoqIFNvbWVvbmUgZWxzZSAoaW5jbHVkaW5nIGZ1dHVyZSB5b3UpIGNhbiB1bmRlcnN0YW5kIHdoYXQgZ29lcyBvbgoKKkhvdz8qCgpSU3R1ZGlvIHByb2plY3RzISAgCkN1c3RvbSBzZXR0aW5ncywgcGVyIHByb2plY3QuCgpMZXQncyBjcmVhdGUgb25lIGxpdmUgbm93IC0gYW5kIGhhdmUgdGhlIHdvcmtzcGFjZSBOT1Qgc2F2ZWQKCipXaGF0IGlzIHRoZSBiZXN0IHN0cnVjdHVyZT8qCgpPbmUsIHVzZWQgY29uc2lzdGVudGx5IC0gbm90IGdvbm5hIHRvdWNoIG9uIG5hbWluZyB0aGluZ3MgYXMgaXQgY2FuIGdldCBob3QgcXVpY2tseSA6KQoKV2l0aCBSLi4uCgpgZGlyLmNyZWF0ZSgpYAoKYGZpbGUuZWRpdCgpYAoKCipXaGVyZSBhbSBJIGRvaW5nIHRoaW5ncz8qCgpUaGUgd29ya2luZyBkaXJlY3RvcnkgaXMgdGhlIHBsYWNlIGZyb20gd2hlcmUgUiB3aWxsIGJlIGxvb2tpbmcgZm9yIGFuZCBzYXZpbmcgdGhlIGZpbGVzLiAKCmBnZXR3ZCgpYC9gc2V0d2QoKWAsIGJ1dCBub3QgaW4geW91ciBzY3JpcHRzIChmYWlscyBvbiBvdGhlcnMnIGNvbXB1dGVycyEpCgojIyBJbnRlcmFjdGluZyB3aXRoIFIKCkluc3RydWN0aW9ucywgY29tbWFuZHMuCgpTY3JpcHRzLCBjb25zb2xlIC0gdXNlIHRoZSBlZGl0b3IgYW5kIGhhdmUgYSBjb21wbGV0ZSByZWNvcmQgb24gd2hhdCB5b3UgZGlkIQoKU2hvcnRjdXRzIEZUVyEKCkV2ZW4gYmV0dGVyOiBSZXByb2R1Y2libGUgZG9jdW1lbnRzLCB3aXRoIFIgTWFya2Rvd24KCk5pY2UgcmVzb3VyY2VzIG9uIHRvcDoKKiBSU3R1ZGlvIGNoZWF0c2hlZXQgYWJvdXQgdGhlIFJTdHVkaW8gSURFIQoqIHRoZSBpbnRlcm5ldC9yc3RhdHMgY29tbXVuaXR5IQoKCiMjIFNlZWtpbmcgaGVscAoKKiA/CiogPz8KKiBidWlsdC1pbiBSU3R1ZGlvIGhlbHAgaW50ZXJmYWNlIC0gYW5kIHNob3J0Y3V0cyEKCgojIyMgV2hlcmUgdG8gYXNrIGZvciBoZWxwPwoKKiB5b3VyIG5laWdoYm91ciAtIGlmIENPVklELWNvbmZvcm0sIGRvIGludGVyYWN0IHdpdGhpbiBlYWNoIG90aGVyIQoqIHlvdXIgY29sbGVhZ3VlcwoqIGh0dHBzOi8vcmRvY3VtZW50YXRpb24ub3JnLyB3ZWJzaXRlIAoqIHRoZSB3ZWI6IGdvb2dsZSwgU3RhY2tPdmVyZmxvdwoKVGhlIG1haW4gcG9pbnQ6IGRlc2NyaWJlIHdlbGwgeW91ciBwcm9ibGVtLCAiaGVscCBvdGhlcnMgaGVscCB5b3UiCgpPdGhlcnMgbmVlZCB0byByZXByb2R1Y2UgeW91ciBlcnJvciB0byBoZWxwIHlvdSBiZXR0ZXI6IGBzYXZlUkRTKClgLCBgZHB1dCgpYCwgYHNlc3Npb25JbmZvKClgCgoKIyMgUiBwYWNrYWdlcwoKUiBwYWNrYWdlcy4uLgoKLSBhcmUgZnVuZGFtZW50YWwgY29tcG9uZW50cyBvZiBSIGVjb3N5c3RlbQotIGV4dGVuZCBiYXNlIFIgZnVuY3Rpb25hbGl0eSBmb3IgYSBzcGVjaWZpYyBwdXJwb3NlIAotIGJ1bmRsZSBuZXcgZnVuY3Rpb25zLCBkYXRhIHNldHMsIGFuZCBkb2N1bWVudGF0aW9uCi0gYXJlIGNvbnRyaWJ1dGVkIGJ5IGluZGVwZW5kZW50IGRldmVsb3BlcnMKLSBoYXZlIGRlcGVuZGVuY3kgbWFuYWdlbWVudAoKUmVwb3NpdG9yaWVzOgoKLSBDUkFOOiBNYW5hZ2VkIG9mZmljaWFsIHBhY2thZ2UgcmVwb3NpdG9yeSBuZXR3b3JrIAotIEJpb2NvbmR1Y3RvcjogY3VyYXRlZCBiaW9pbmZvcm1hdGljcyBwYWNrYWdlcyAodmlnbmV0dGVzIG1hbmRhdG9yeSwgaW50ZWdyYXRlZCBlY29zeXN0ZW0hKQotIEdpdEh1YjogdW4tbWFuYWdlZCwgYmxlZWRpbmcgZWRnZSAtIGJ1dCBhbHNvIGV4Y2VsbGVudCBvbmVzICgianVzdCBub3Qgb24gQ1JBTiIpCgoKT3VyIGNvbnRyaWJ1dGlvbnMgc28gZmFyOgoKLSBmbG93Y2F0Y2hSIGBodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZmxvd2NhdGNoUi9gCi0gcGNhRXhwbG9yZXIgYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9wY2FFeHBsb3Jlci9gCi0gaWRlYWwgYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9pZGVhbC9gCi0gR2VuZVRvbmljIChgaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL0dlbmVUb25pY2ApCi0gaVNFRSAoYGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9pU0VFYCkKLSBzaW1yZWMgKGZvciBzaW11bGF0aW5nIHJlY3VycmluZyBldmVudHMsIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zaW1yZWMvKQoKCiMjIyAgSG93IHRvIHVzZSBwYWNrYWdlcwoKLSAqKkluc3RhbGwqKjogb25jZSAoZm9yIGV2ZXJ5IG1ham9yIFIgdmVyc2lvbikKLSAqKkxvYWQqKjogaW4gZWFjaCBzZXNzaW9uCi0gKipVc2UqKiBsaWtlIGFueSBiYXNlIFIgZnVuY3Rpb25hbGl0eQoKRm9yIEJpb2NvbmR1Y3RvciBwYWNrYWdlcy4uLgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeSgiQmlvY01hbmFnZXIiKQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgpCmBgYAoKClJlbGV2YW50IGNvbW1hbmRzOiAKCi0gYGluc3RhbGwucGFja2FnZXMoInBhY2thZ2VuYW1lIilgIC0gY2hlY2sgaXQgb25saW5lIGF0IENSQU4hCi0gYGluc3RhbGxlZC5wYWNrYWdlcygpYAotIGAubGliUGF0aHMoKWAKLSBgdXBkYXRlLnBhY2thZ2VzKClgCi0gYGxpYnJhcnkoInBhY2thZ2VuYW1lIilgCi0gYGhlbHAocGFja2FnZT0icGFja2FnZW5hbWUiKWAsIGBkYXRhKClgLCBgYnJvd3NlVmlnbmV0dGVzKClgLCBgdmlnbmV0dGUoKWAsIGBjaXRhdGlvbigicGFja2FnZW5hbWUiKWAKClNvbWV0aGluZyB5b3UgbWlnaHQgaGF2ZSBhbHJlYWR5IGRvbmUsIGlmIHlvdSB3b3JrZWQgb24gUk5BLXNlcSBkYXRhOgoKYGBge3IgZXZhbD1GQUxTRX0KQmlvY01hbmFnZXI6Omluc3RhbGwoIlN1bW1hcml6ZWRFeHBlcmltZW50IikKQmlvY01hbmFnZXI6Omluc3RhbGwoIkRFU2VxMiIpCmBgYAoKIyMgSG93IHNob3VsZCBJIHVzZSB0aGlzIG1hdGVyaWFsPwoKVXNlIHRoaXMgdmVyeSBzYW1lIHRleHQgZG9jdW1lbnQgYW5kIGV4cGFuZCB0aGlzIQoKV2h5PyBMZXQncyBoYXZlIGEgbG9vayBmaXJzdCBpbnRvIHRoZSBCb251cyBDb250ZW50LCBhY3R1YWxseTogd2UgbmF2aWdhdGUgdG8gU2VjdGlvbiBcQHJlZihyZXByb3JlcG9ydHMpIGFuZCBjb250aW51ZSBmcm9tIHRoZXJlLgoKCiMgU3RlcCAxOiBJbnRyb2R1Y3Rpb24gdG8gUiB7I3N0ZXAxfQoKSGVyZSB3ZSB3aWxsIHRvdWNoIG9uIHRoZSBmaXJzdCBjb21tYW5kcyBpbiBSLCBzbyB0aGF0IHlvdSBjYW4KCiogRGVmaW5lIHRoZSBmb2xsb3dpbmcgdGVybXMgYXMgdGhleSByZWxhdGUgdG8gUjogb2JqZWN0LCBhc3NpZ24sIGNhbGwsIGZ1bmN0aW9uLCBhcmd1bWVudHMsIG9wdGlvbnMuCiogQXNzaWduIHZhbHVlcyB0byBvYmplY3RzIGluIFIuCiogTGVhcm4gaG93IHRvIG5hbWUgb2JqZWN0cwoqIFVzZSBjb21tZW50cyB0byBpbmZvcm0gc2NyaXB0LgoqIFNvbHZlIHNpbXBsZSBhcml0aG1ldGljIG9wZXJhdGlvbnMgaW4gUi4KKiBDYWxsIGZ1bmN0aW9ucyBhbmQgdXNlIGFyZ3VtZW50cyB0byBjaGFuZ2UgdGhlaXIgZGVmYXVsdCBvcHRpb25zLgoqIEluc3BlY3QgdGhlIGNvbnRlbnQgb2YgdmVjdG9ycyBhbmQgbWFuaXB1bGF0ZSB0aGVpciBjb250ZW50LgoqIFN1YnNldCBhbmQgZXh0cmFjdCB2YWx1ZXMgZnJvbSB2ZWN0b3JzLgoqIEFuYWx5emUgdmVjdG9ycyB3aXRoIG1pc3NpbmcgZGF0YS4KCgojIyBSIGlzIGEgcG93ZXJmdWwgY2FsY3VsYXRvcgoKLi4uIGJ1dCBub3QganVzdCB0aGF0LgoKVHlwZSB0aGUgZm9sbG93aW5nCgpgYGB7cn0KMiArIDIKbG9nKDIpCjM0NyAqIDczODQxCjcgLyAyCjcgJS8lIDIKNyAlJSAyCmBgYAoKV2hhdCBkaWQgdGhlc2UgY29tbWFuZHMgZG8/CgojIyBgciBlbW86OmppKCJub3RlcyIpYCBIZWxwISBgciBlbW86OmppKCJub3RlcyIpYAoKYGBge3J9CiMgdGhpcyBjYWxscyB0aGUgaGVscCBmb3IgYSBmdW5jdGlvbiB0byBwbG90IGEgaGlzdG9ncmFtCj9oaXN0CiMgdGhpcyBpcyBqdXN0IHRoZSBzYW1lCmhlbHAoaGlzdCkgIyMgd2hhdCBhYm91dCA/PwpgYGAKCmBgYHtyfQo/YXByb3BvcwphcHJvcG9zKCJyb3ciKQpgYGAKCi0gaW50ZWdyYXRlZCBoZWxwIHN5c3RlbSwgd2l0aCBleGVjdXRhYmxlIGV4YW1wbGVzCi0gKGZvciBzb21lIHBhY2thZ2VzKSB2aWduZXR0ZXMgKHR5cGljYWwgcHJvYmxlbSwgY29tbWFuZHMsIGFuZCB3b3JrZmxvdykKLSBDUkFOIFRhc2sgVmlld3M6IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi92aWV3cy8KLSBCb29rcyEKLSBDb3Vyc2VzIQotIE9ubGluZTogbWFpbGluZyBsaXN0cywgZm9ydW1zIChTdGFja092ZXJmbG93LCAuLi4pLCBibG9ncywgVHdpdHRlciAoYCNyc3RhdHNgKQoKCiMjIFlvdXIgc3RhcnRpbmcgdm9jYWJ1bGFyeSAtIGEuay5hLiBFeGVyY2lzZSBTZXNzaW9uIDAKCi0gYGdldHdkKClgIGFuZCBgc2V0d2QoKWAgLSBUYWIgaXMgeW91ciBmcmllbmQKLSBgIDwtIGAsIGAgPSBgOiB0aGUgYXNzaWdubWVudCBvcGVyYXRvcgotIGBscygpYCwgYHJtKClgCi0gYHN0cigpYAotIGBleGFtcGxlKClgLCBgaGVscCgpYC9gP1tmdW5jdGlvbl1gCi0gYHByaW50KClgCi0gYHEoKWAvYHF1aXQoKWAKLSBsb2dpY2FsIG9wZXJhdG9yczogYFRSVUVgLGBGQUxTRWAsYCFgLGA9PWAsYCE9YCxgPGAsYD5gLGA8PWAsYD49YCxgfGAsYCZgLGB4b3IoKWAKLSBgYygpYAotIGRhdGEgaGF2ZSBoZWxwIGl0ZW1zIHRvbzogZS5nLiBgY2Fyc2AKCkZpbmQgb3V0IHdoYXQgdGhlc2UgZG8hCgojIyMgRXhlcmNpc2UgU2Vzc2lvbiAwIC0gU29sdXRpb25zCgo8ZGV0YWlscz4KPHN1bW1hcnk+Q2xpY2sgb24gdGhpcyB0byBkaXNwbGF5IHRoZSBzb2x1dGlvbjwvc3VtbWFyeT4KCmBgYHtyfQo/Z2V0d2QKP3NldHdkCj9gPC1gCmhlbHAobHMpCmhlbHAocm0pCj9zdHIKP2V4YW1wbGUKaGVscChoZWxwKQo/cHJpbnQKaGVscChxdWl0KQo/Ywo/Y2FycwpgYGAKCjwvZGV0YWlscz4KCiMjIE1ha2UgeW91ciBsaWZlIGVhc2llciAtIE5vdGVzIGZvciB5b3VyIGZ1dHVyZSBzZWxmCgotIGFkZCBjb21tZW50cyBhbmQgZG9jdW1lbnQgeW91ciBvd24gY29kZQoKYGBge3J9CiMgVGhpcyBpcyBhIGNvbW1lbnQKYGBgCgotIHdyaXRlIGNsZWFuIGNvZGUgLSB1c2Ugc3BhY2VzLCBpbmRlbnRhdGlvbgotIHVzZSBhbiBlZGl0b3Igd2l0aCBzeW50YXggaGlnaGxpZ2h0aW5nL3NvbWUgZm9ybSBvZiBhdXRvY29tcGxldGlvbiAKCkNhcmVmdWwgaGVyZToKCi0gUiBpcyBjYXNlIHNlbnNpdGl2ZSBhbmQgaGFzIHplcm8tdG9sZXJhbmNlIHdpdGggbWlzLXNwZWxsZWQgbmFtZXMKLSBwYXJlbnRoZXNpczogb3BlbiAqYW5kKiBjbG9zZSB0aGVtCi0gc3BlY2lhbCBhdHRlbnRpb24gd2l0aCBtaXNzaW5nIHZhbHVlcywgZmFjdG9ycyBWUyBzdHJpbmdzOiBSIGlzIGNsZXZlciwgYnV0IHlvdSBtaWdodCB0aGluayBkaWZmZXJlbnRseQotIGRvIG5vdCBiZSBzdGluZ3kgd2l0aCBwYXJlbnRoZXNlcyAtIGlmIHRoaXMgaGVscHMgeW91Ci0gc2FtZSBnb2VzIHdpdGggY29tbWVudHMgLSB5b3VyIGNvbGxlYWd1ZXMgYW5kIHlvdXIgZnV0dXJlIHNlbGYgd2lsbCB0aGFuayB5b3UKCgojIyBFeGVyY2lzZSBzZXNzaW9uIDEKCkZpcnN0IHRoaW5ncyBmaXJzdDoKCiogR3JhYiBzb21lIG1pbmktcG9zdGl0IChmb3IgdGhvc2Ugb2YgeW91IHBhcnRpY2lwYXRpbmcgaW4gcGVyc29uKSEKKiBHZXQgcmVhZHkgdG8gdXNlIHRoZSBjaGF0ICYgdGhlIHJlYWN0aW9ucyAKClRoZW46CgotIGZpbmQgb3V0IG1vcmUgYWJvdXQgdGhlIGBpcmlzYCBkYXRhc2V0LiBXaGF0IGlzIGl0IGFib3V0IGF0IGFsbD8gSG93IG1hbnkgdmFyaWFibGVzIGFyZSBpbmNsdWRlZD8gSG93IG1hbnkgb2JzZXJ2YXRpb25zPwotIGByZXBgbGljYXRlISBmaW5kIG91dCBhIGZ1bmN0aW9uIHRoYXQgYHJlcGBsaWNhdGVzIGVsZW1lbnRzIG9mIGEgdmVjdG9yIHRvIHByb2R1Y2UgdGhpcwoKYGBgCjEgMSAxIDEKYGBgCgpCT05VUzogLi4uIGFuZCB0aGlzIAogCmBgYAoxIDIgMyA0IDUgMSAyIDMgNCA1IDEgMiAzIDQgNQpgYGAKCiMjIyBFeGVyY2lzZSBTZXNzaW9uIDEgLSBTb2x1dGlvbnMKCjxkZXRhaWxzPgo8c3VtbWFyeT5DbGljayBvbiB0aGlzIHRvIGRpc3BsYXkgdGhlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKYGBge3J9CnJlcCgxLCA0KQoKcmVwKGMoMSwgMiwgMywgNCwgNSksIDMpCmBgYAoKPC9kZXRhaWxzPgoKIyMgRGF0YSB0eXBlcyAKClIgY2FuIHJlY29nbml6ZSBkaWZmZXJlbnQgZ2VuZXJhbCB0eXBlcyBvZiBkYXRhCgotIG51bWJlcnMgKGBudW1lcmljYCkKLSBjaGFyYWN0ZXIgc3RyaW5ncyAodGV4dCkKLSBsb2dpY2FsIChlLmcuIGBjbGFzcyhUUlVFKWApCi0gZmFjdG9ycyAoImludGVnZXJzIHdpdGggYSBzZXQgb2YgbGFiZWxzIikgLSBpdCBpcyBjYXRlZ29yaWNhbCBkYXRhIQoKLSBzcGVjaWFsIG9uZXM6IGRhdGVzLCB0aW1lLCAuLi4KIApXaGVuIGFuZCB3aGVyZSB0byB1c2Ugd2hpY2g/IAoKCiMjIEkgd2FzIGN1cmlvdXMgYWJvdXQgdGhlIHBhcnRpY2lwYW50cwoKSW4gYSBwcmV2aW91cyBlZGl0aW9uIG9mIGEgc2ltaWxhciBjb3Vyc2UsIEkgYXNrZWQgdGhlIGZ1dHVyZSBwYXJ0aWNpcGFudHM6CgotIEhvdyBvbGQgYXJlIHlvdT8KLSBXaGF0IGlzIHlvdXIgY3VycmVudCBhY2FkZW1pYyBsZXZlbD8gKFBJLCBQb3N0ZG9jLCBQaEQsIG1hc3RlcikKLSBXaGF0IGlzIHlvdXIgY3VycmVudCBrbm93bGVkZ2UgbGV2ZWwgb2YgUj8gKHByby1nb29kLWludGVybWVkaWF0ZS1wb29yLW5vbmUpCi0gV2hhdCBpcyB5b3VyIGtub3dsZWRnZSBvZiBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaW4gZ2VuZXJhbD8KLSBXaGF0IGlzIHlvdXIgZXhwZXJpZW5jZSBsZXZlbCB3aXRoIGdlbm9taWNzIGFuZCBSTkEtc2VxIGRhdGE/Ci0gSG93IGZhbWlsaWFyIGFyZSB5b3Ugd2l0aCBtb2dvbiBhbmQgcGFyYWxsZWwgY29tcHV0aW5nPyAoSSBhbSBhIHJlZ3VsYXIgdXNlci9PbmNlIGluIGEgd2hpbGUgaSB1c2VkIGl0L0kga25vdyBpdCBleGlzdHMvSSBoZWFyZCB3ZSBoYWQgc29tZSBzZXJ2ZXJzIGFyb3VuZC9JcyB0aGlzIHN1cHBvc2VkIHRvIGJlIGluIHRoZSBjbG91ZD8hKQotIFdoYXQgYXJlIHlvdXIgZXhwZWN0YXRpb25zIGZyb20gdGhlIGNvdXJzZT8gCgoKIyBTdGVwIDI6IERhdGEgaW4sIGRhdGEgb3V0IHsjc3RlcDJ9CgojIyBJbXBvcnRpbmcgZGF0YSBpbiBSCgo4MC0yMD8gOTAtMTA/IEltcG9ydCwgY2xlYW4sIHByZXBhcmUsIHRyYW5zZm9ybSB5b3VyIGRhdGEKClNvdXJjZXM6CgotIEZpbGVzLCBDbGlwYm9hcmQsIFVSTAotICoqUGxhaW4gdGV4dCBmaWxlOiBDb21tYS1zZXBhcmF0ZWQsIHRhYi1kZWxpbWl0ZWQsIC4uLioqCi0gUiBmb3JtYXQgZmlsZQotIFNBUyAvIFN0YXRhIC8gU1BTUyBmaWxlOiBwYWNrYWdlIGBoYXZlbmAKLSBTcHJlYWRzaGVldCAoRXhjZWwpOiBwYWNrYWdlIGByZWFkeGxgIC0gaGlnaGx5IHJlY29tbWVuZGVkIQotIERhdGFiYXNlOiBSU1FMaXRlLCBSUG9zdGdyZVNRTCwgUk15U1FMLCAuLi4KCgojIyBUaGUgdm9jYWJ1bGFyeSBvZiBpbXBvcnRpbmcKCi4uLiBhbmQgZXhwb3J0aW5nCgotIGByZWFkLnRhYmxlKClgLGB3cml0ZS50YWJsZSgpYCArIGByZWFkLmNzdnxkZWxpbWAKLSB0aGUgb3B0aW9uIGBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFYAotIGBsb2FkKClgLGBzYXZlKClgL2ByZWFkUkRTKClgLGBzYXZlUkRTKClgCi0gdmlhIGBoYXZlbmAgOiBgcmVhZF9zYXMoKWAsYHJlYWRfc3BzcygpYCAvYHdyaXRlX3NhcygpYCxgd3JpdGVfc2F2KClgCi0gdmlhIGByZWFkeGxgOiBgcmVhZF9leGNlbCgpYAoKQ2hlY2sgb3V0IHRoZWlyIGRvY3VtZW50YXRpb24gcGFnZXMhCgpPdGhlciBvcHRpb25zOiBgcmlvYCwgUlN0dWRpbyBHVUkKCgoKIyMgVGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEgCgpHbyB0byBodHRwczovL2dpdGh1Yi5jb20vZmVkZXJpY29tYXJpbmkvcmJpb2MyMDE2CgokXHJpZ2h0YXJyb3ckIGBpbnN0L2V4dGRhdGFgCgokXHJpZ2h0YXJyb3ckIGBzdXJ2ZXlfcmVzcG9uc2VzLmNzdmAsIGluIGl0cyByYXcgZm9ybWF0CgpZb3UgY2FuIGxvYWQgaXQgZGlyZWN0bHkgbGlrZSB0aGlzCgpgYGB7cn0Kc3VydmV5cmJpb2MgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9mZWRlcmljb21hcmluaS9yYmlvYzIwMTYvbWFzdGVyL2luc3QvZXh0ZGF0YS9zdXJ2ZXlfcmVzcG9uc2VzLmNzdiIpCmBgYAoKT3IgaW5zdGFsbCB0aGUgcGFja2FnZSBhbmQgbG9hZCBpdCBmcm9tIHRoZXJlCgpgYGB7ciBldmFsPUZBTFNFfQpsaWJyYXJ5KCJkZXZ0b29scyIpCmluc3RhbGxfZ2l0aHViKCJmZWRlcmljb21hcmluaS9yYmlvYzIwMTYiKQpsaWJyYXJ5KCJyYmlvYzIwMTYiKQpkYXRhKHN1cnZleXJiaW9jKQpgYGAKCgojIyBJbnB1dCBkYXRhOiBTdGVwIGJ5IHN0ZXAsIGJ5IGhhbmQ/CgpTb21ldGltZXMgeW91ciBkYXRhIGlzIGVpdGhlciBzbWFsbCBhbmQvb3Igbm90IGluIGFuIEV4Y2VsLWxpa2UgdGFidWxhciBmb3JtYXQuCgpXaGF0IHRvIGRvPyBZb3UgYGNgb21iaW5lIHRoZSBlbGVtZW50cyB0b2dldGhlciEKCmBgYHtyIGV2YWw9VFJVRX0KUTEgPC0gYygyOCwgMjcsIDMzLCAzMiwgMjkpCiMgc2hvdWxkIHJldHVybiB0aGlzClExCgpRMiA8LSBjKCJQaEQgc3R1ZGVudCIsICJQaEQgc3R1ZGVudCIsICJQb3N0ZG9jIiwgIlBoRCBzdHVkZW50IiwgIlBoRCBzdHVkZW50IikKUTIKIyAuLi4gYW5kIHNvIG9uCmBgYAoKCiMjIENvbWJpbmUgdGhlIHZhcmlhYmxlcyB0byBhIG1hdHJpeAoKV2UgaGF2ZSBzZWVuIGBjKClgLiBXZSBhbHNvIGhhdmUKCi0gYGNiaW5kYAotIGByYmluZGAKCmBgYHtyLCBldmFsPVRSVUV9CmZpcnN0VHdvIDwtIGNiaW5kKFExLCBRMikKZmlyc3RUd28KYGBgCgpgYGB7cn0KcmJpbmQoUTEsIFEyKQpgYGAKCklzIHRoaXMgd2hhdCB5b3Ugd2FudGVkPwoKIyMgQXBwbHlpbmcgdGhlIGZpcnN0IGZ1bmN0aW9ucwoKQnV0IGZpcnN0LCB3aGF0IGNhbiB5b3UgZG8gb24gdGhlc2Ugb2JqZWN0cz8KCmBgYHtyIGVycm9yPVRSVUV9CnN1bShRMSkKc3VtKFEyKQpzdW1tYXJ5KFExKQpzdW1tYXJ5KFEyKQpzdHIoUTEpCnN0cihRMikKbWVhbihRMSkKZGltKGZpcnN0VHdvKQpmaXJzdFR3b1ssIDFdCm1lYW4oZmlyc3RUd29bLCAxXSkgIyBXaHksIGRhbW4sIHdoeT8gTWVldCBjb2VyY2lvbgpjbGFzcyhmaXJzdFR3bykKYGBgCgojIyBgbWF0cml4YCwgYGRhdGEuZnJhbWVgIGFuZCBgbGlzdGAKCi0gYSBgbWF0cml4YCBjYW4gY29udGFpbiBvbmUgdHlwZSBvZiBkYXRhIC0gaWYgbnVtZXJpYywgeW91IHVubGVhc2ggYWxsIHRoZSBtYXRyaXggYWxnZWJyYSBwb3dlciEKLSBhIGBkYXRhLmZyYW1lYCBjYW4gc3RvcmUgbW9yZSB0eXBlcyBvZiBkYXRhIChvbmUgcGVyIGNvbHVtbikKLSBhIGBsaXN0YCBpcyBsaWtlIGEgYmlnIGJveCB3aGVyZSB5b3UgY2FuIHB1dCBhbnl0aGluZyAtIGJ1dCB0aGlzIGlzIG5vdCBhbHdheXMgd2hhdCB5b3Ugd2FudAoKV2hhdCBpcyBiZXN0PwoKTGV0J3MgdHJ5IHdpdGggYSBgbGlzdGAKCmBgYHtyIGV2YWw9VFJVRX0KUTMgPC0gYygiaW50ZXJtZWRpYXRlIiwgInBvb3IiLCAiZ29vZCIsICJub25lIiwgImludGVybWVkaWF0ZSIpCm15bGlzdCA8LSBsaXN0KFExLCBRMiwgUTMpCm15bGlzdApgYGAKCmBgYHtyfQojIyBhY2Nlc3MgeW91ciBlbGVtZW50cyB3aXRoCm15bGlzdFtbMV1dCm15bGlzdFtbMV1dWzJdCmBgYAoKCkhvdyBkbyB3ZSBjcmVhdGUgYSBgZGF0YS5mcmFtZWA/CgpgYGB7ciBldmFsPVRSVUV9Cm15ZGYgPC0gZGF0YS5mcmFtZSgKICBhZ2UgPSBRMSwKICBsZXZlbCA9IFEyLAogIHJleHAgPSBRMwopCm15ZGYKY2xhc3MobXlkZiRhZ2UpCmBgYAoKCiMjIyBFeHBsb3JpbmcgYSBgZGF0YS5mcmFtZWAKCmBgYHtyfQpteWRmJGFnZSAjIGl0J3MgYWxsIGFib3V0IHRoZSBtb25leSA6KQpteWRmWywgMV0KbmFtZXMobXlkZikKcm93bmFtZXMobXlkZikKZGltKG15ZGYpCm5yb3cobXlkZikKbmNvbChteWRmKQpgYGAKCmBgYHtyIHRpZHk9VFJVRX0Kc3VydmV5cmJpb2MgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9mZWRlcmljb21hcmluaS9yYmlvYzIwMTYvbWFzdGVyL2luc3QvZXh0ZGF0YS9zdXJ2ZXlfcmVzcG9uc2VzLmNzdiIpCmhlYWQoc3VydmV5cmJpb2MpCnRhaWwoc3VydmV5cmJpb2MpCm5hbWVzKHN1cnZleXJiaW9jKQpzdHIoc3VydmV5cmJpb2MpCnN1bW1hcnkoc3VydmV5cmJpb2MpCnN1cnZleXJiaW9jWywgXQpgYGAKCkRvbid0IGZvcmdldCBzb21lIG5pY2UgYnVpbHQtaW4gbGlrZSB0aGUgYFZpZXcoKWAgZnVuY3Rpb24sIGluIFJTdHVkaW8gLSB0aGlzIGlzIGEgbmljZSBhbHRlcm5hdGl2ZSB0byAicHVyZSBicm93c2luZyBpbiBFeGNlbCIuCgoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiAyCgpVc2luZyB0aGUgYHN1cnZleXJiaW9jYCBvYmplY3Q6CgotIENhbGN1bGF0ZSB0aGUgbWVhbiBhZ2Ugb2YgdGhlIHBhcnRpY2lwYW50cwotIEhvdyBtYW55IHBhcnRpY2lwYW50cyBkaWQgYWN0dWFsbHkgdGFrZSBwYXJ0IHRvIHRoZSBzdXJ2ZXk/Ci0gSG93IG9sZCB3YXMgdGhlIG9sZGVzdCBwYXJ0aWNpcGFudD8gKGBtYXhgIGNhbiBiZSB5b3VyIGhlbHApCi0gYHRgcmFuc3Bvc2UgdGhlIHN1cnZleSBkYXRhIGFuZCBhc3NpZ24gaXQgdG8gYW5vdGhlciB2YXJpYWJsZQotIENoYW5nZSB0aGUgY29sdW1uIG5hbWVzIG9mIHRoaXMgb2JqZWN0IGFuZCBzYXZlIHRoaXMgZGF0YSBzZXQgYXMgYSB0YWItc2VwYXJhdGVkIEFTQ0lJIGZpbGUKLSBCT05VUzogd2hhdCB3YXMgdGhlIHlvdW5nZXN0IHBhcnRpY2lwYW50IGV4cGVjdGluZz8KCiMjIyBFeGVyY2lzZSBTZXNzaW9uIDIgLSBTb2x1dGlvbnMKCjxkZXRhaWxzPgo8c3VtbWFyeT5DbGljayBvbiB0aGlzIHRvIGRpc3BsYXkgdGhlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKYGBge3J9Cm1lYW4oc3VydmV5cmJpb2MkUTEpCgptYXgoc3VydmV5cmJpb2MkUTEpCgpteV90cmFuc3Bvc2VkX3N1cnZleSA8LSB0KHN1cnZleXJiaW9jKQoKc3VydmV5cmJpb2NfbW9kIDwtIHN1cnZleXJiaW9jCmNvbG5hbWVzKHN1cnZleXJiaW9jX21vZCkgPC0gYygiYWdlIiwgImxldmVsIiwgInJsZXZlbCIsICJwcm9nX2xldmVsIiwgImdlbm9taWNzX2xldmVsIiwgInBhcmNvbXBfbGV2ZWwiLCAiZXhwZWN0YXRpb24iKQoKc3VydmV5cmJpb2NfbW9kJGV4cGVjdGF0aW9uW3doaWNoLm1pbihzdXJ2ZXlyYmlvY19tb2QkYWdlKV0KYGBgCgo8L2RldGFpbHM+CgojIFN0ZXAgMzogQW5hbHl6aW5nICh0YWJ1bGFyKSBkYXRhIHsjc3RlcDN9CgpEZXNjcmliZSwgZXhwbG9yZSwgdHJhbnNmb3JtLCBzdW1tYXJpc2UgZGF0YQoKIyMgRXhwbG9yaW5nLCBzdWJzZXR0aW5nLCBtYW5pcHVsYXRpbmcsIGFuYWx5c2luZwoKLSBgZGltKHgpYCBzaG93cyB0aGUgZGltZW5zaW9ucyBvZiBhbiBvYmplY3QKLSBgc3RyKHgpYCBwcm92aWRlcyBhbiBvdmVydmlldyBvZiB0aGUgc3RydWN0dXJlIG9mIGFuIG9iamVjdCBhbmQgdGhlIGVsZW1lbnRzIGl0IGNvbnRhaW5zCi0gYHN1bSh4KWAsIGBtZWFuKHgpYCwgYHNkKHgpYCBjb21wdXRlcyB0aGUgc3VtLCBtZWFuLCBvciBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYWxsIHRoZSBlbGVtZW50cyBpbiBgeGA7IGBtZWRpYW4oeClgLCBgcXVhbnRpbGUoeClgCi0gYGxlbmd0aCh4KWAgcmV0dXJucyB0aGUgbnVtYmVyIG9mIGVsZW1lbnRzIGluIHggKGEgdmVjdG9yKQotIGBzcXJ0KHgpYCwgYGxvZyh4KWAgdGFrZSB0aGUgc3F1YXJlIHJvb3QgYW5kIHRoZSBuYXR1cmFsIGxvZ2FyaXRobSBvZiBhIG51bWVyaWMgLSBlbGVtZW50IG9yIHZlY3RvcgotIGBoaXN0KHgsIGJyZWFrcz0yMCwgY29sPSJibHVlIilgIHBsb3RzIGEgaGlzdG9ncmFtIG9mIHZhcmlhYmxlIHggd2l0aCAyMCBiaW5zIGNvbG9yZWQgYmx1ZQotIGB1bmlxdWUoeClgIHJldHVybnMgdGhlIHZlY3RvciBvZiB1bmlxdWUgZWxlbWVudHMgaW4geAotIGBybSh4KWAgcmVtb3ZlcyB0aGUgb2JqZWN0IHggZnJvbSB0aGUgZW52aXJvbm1lbnQgKGBybShsaXN0PWxzKCkpYCByZW1vdmVzIGFsbCBvYmplY3RzKQotIGBzZXNzaW9uSW5mbygpYCBwcmludHMgaW5mb3JtYXRpb24gYWJvdXQgUiBzZXNzaW9uIGFuZCB2ZXJzaW9ucyBvZiBhbGwgYXR0YWNoZWQgcGFja2FnZXMKLSBsb2dpY2FsIG9wZXJhdG9ycyBtaWdodCBvZnRlbiBjb21lIGhhbmR5IQoKCiMjIFN1YnNldHRpbmcgdGhlIGRhdGEKClRoaXMgaXMgdGhlIGJhc2ljIHdheSBpdCB3b3JrcwoKYGBge3IgZXZhbD1GQUxTRX0Kc3VydmV5cmJpb2NbUk9XUywgQ09MVU1OU10KYGBgCgpZb3UgY2FuIHN1YnNldCB3aXRoLi4uCgotIGludGVnZXJzCi0gYmxhbmsgc3BhY2VzCi0gbmFtZXMKLSBsb2dpY2FsIHZlY3RvcnMKClRyeSB0byBtYWtlIGEgZ3Vlc3MsIGdpdmVuIHRoaXMgdmVjdG9yLgoKYGBge3IgZXZhbD1UUlVFfQp2ZWMgPC0gYyg2LCAxLCAzLCA2LCAxMCwgNSkKYGBgCgpXaGF0IGhhcHBlbnMgaWYgeW91IGRvIHRoaXM/CgpgYGB7cn0KdmVjWzJdCnZlY1tjKDUsIDYpXQp2ZWNbLWMoNSwgNildCnZlYyA+IDUKdmVjW3ZlYyA+IDVdCmBgYAoKCldoYXQgaGFwcGVucyBpZiB5b3UgZG8gdGhpcz8KCmBgYHtyfQpkZiA8LSBkYXRhLmZyYW1lKAogIG5hbWUgPSBjKCJKb2huIiwgIlBhdWwiLCAiR2VvcmdlIiwgIlJpbmdvIiksCiAgYmlydGggPSBjKDE5NDAsIDE5NDIsIDE5NDMsIDE5NDApLAogIGluc3RydW1lbnQgPSBjKCJndWl0YXIiLCAiYmFzcyIsICJndWl0YXIiLCAiZHJ1bXMiKQopCgpkZgoKZGZbYygyLCA0KSwgM10KZGZbLCAxXQpkZlssICJpbnN0cnVtZW50Il0KZGYkaW5zdHJ1bWVudApgYGAKCkJhY2sgdG8gdGhlIHN1cnZleQoKYGBge3J9CiMgSSBqdXN0IHdhbnQgdGhlIGFnZQpzdXJ2ZXlyYmlvY1ssIDFdCiMgb3IKc3VydmV5cmJpb2MkUTEKCiMgdGhlIGZpcnN0IDQgY29sdW1ucwpzdXJ2ZXlyYmlvY1ssIGMoMSwgMiwgMywgNCldCnN1cnZleXJiaW9jWywgMTo0XQoKIyBhbGwgYnV0IHRoZSBsYXN0IGNvbHVtbgpzdXJ2ZXlyYmlvY1ssIC03XQojIGlmIHlvdSBkb24ndCBrbm93IHdlIGhhZCA3IGNvbHVtbnMuLi4Kc3VydmV5cmJpb2NbLCAtbmNvbChzdXJ2ZXlyYmlvYyldCgojIHlvdSBjYW4gc3Vic2V0IHdpdGggbG9naWNhbCB2ZWN0b3JzLCBieSByb3cgYW5kIGJ5IGNvbHVtbgpzdXJ2ZXlyYmlvY1tjKHJlcChUUlVFLCAxMCksIHJlcChGQUxTRSwgOCkpLCBdCnN1cnZleXJiaW9jW2MoVFJVRSwgRkFMU0UpLCBdICMga2VlcCBpbiBtaW5kIHRoaXMgYmVoYXZpb3IhCgojIGd1ZXNzIHdoYXQgdGhpcyBkb2VzPwpzdXJ2ZXlyYmlvYyRRMiA9PSAiUGhEIHN0dWRlbnQiCmBgYAoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiAzCgotIEhvdyBtYW55IFBoRCBzdHVkZW50cyBkaWQgcmVwbHk/Ci0gV2hhdCB3YXMgdGhlIHByb3BvcnRpb24gb2YgUGhEIHN0dWRlbnRzIHRvIGFsbCBvdGhlciBwYXJ0aWNpcGFudHM/Ci0gSG93IG9sZCB3ZXJlIHRoZXkgb24gYXZlcmFnZT8KLSBIb3cgbWFueSBvZiB0aGUgcGFydGljaXBhbnRzIHdlcmUgb2xkZXIgdGhhbiAzMD8KLSBIb3cgbWFueSBwb3N0ZG9jcyB3ZXJlIHlvdW5nZXIgdGhhbiAzNT8KLSBIb3cgbWFueSBvZiB0aGUgcGFydGljaXBhbnRzIGRpZCBub3QgcmVwbHkgdG8gdGhlIGxhc3QgcXVlc3Rpb24/IDwhLS0gKGBpcy5uYWAgaXMgeW91ciBmcmllbmQpIC0tPgoKIyMjIEV4ZXJjaXNlIFNlc3Npb24gMyAtIFNvbHV0aW9ucwoKPGRldGFpbHM+CjxzdW1tYXJ5PkNsaWNrIG9uIHRoaXMgdG8gZGlzcGxheSB0aGUgc29sdXRpb248L3N1bW1hcnk+CgpgYGB7cn0Kc3VtKHN1cnZleXJiaW9jJFEyID09ICJQaEQgc3R1ZGVudCIpCm1lYW4oc3VydmV5cmJpb2MkUTIgPT0gIlBoRCBzdHVkZW50IikKbWVhbihzdXJ2ZXlyYmlvYyRRMVtzdXJ2ZXlyYmlvYyRRMiA9PSAiUGhEIHN0dWRlbnQiXSkKc3VtKHN1cnZleXJiaW9jJFExID49IDMwKQpzdW0oc3VydmV5cmJpb2MkUTEgPCAzNSAmIHN1cnZleXJiaW9jJFEyID09ICJQb3N0ZG9jIikKc3VtKGlzLm5hKHN1cnZleXJiaW9jJFE3KSkKYGBgCgo8L2RldGFpbHM+CgojIyBNYW5pcHVsYXRpbmcgYW5kIGFuYWx5c2luZyB5b3VyIGRhdGEKCllvdSBjYW4KCi0gc29ydCB0aGUgZGF0YSAoc2VlIGBzb3J0YCBhbmQgYG9yZGVyYCkKLSB0cmFuc2Zvcm0geW91ciBkYXRhOiBhcHBseSBydWxlcyAoZm9ybXVsYXMsIGxvZ2ljcywgaW5zaWdodCBhbHRvZ2V0aGVyKQotIGNvbWJpbmUgdHdvIGRhdGFzZXRzIG9yIG1vcmUgKGlmIHlvdSBgbWVyZ2VgIHRoZW0pCi0gZG8gc29tZSBzdGF0aXN0aWNzIG9uIHlvdXIgZGF0YQoKCiMjIFNvcnRpbmcgdGhlIGRhdGEKCmBgYHtyIGV2YWw9VFJVRX0KbXlvcmQgPC0gb3JkZXIoc3VydmV5cmJpb2MkUTEpCm15b3JkCgpoZWFkKHN1cnZleXJiaW9jW215b3JkLCAxOjVdLCA0KQpzb3J0ZWRfc3VydiA8LSBzdXJ2ZXlyYmlvY1tteW9yZCwgMTo2XQpgYGAKCmBzb3J0KClgIHJldHVybnMgeW91IHRoZSBzb3J0ZWQgZGF0YSwgYG9yZGVyKClgIHRoZSBpbmRpY2VzIG9ubHkKCgojIyBUcmFuc2Zvcm1pbmcgdGhlIGRhdGEgCgpgYGB7ciBldmFsPVRSVUV9CiMgdHJhbnNmb3JtaW5nIGEgdmFyaWFibGUKbmV3c3VydmV5IDwtIHN1cnZleXJiaW9jWywgMTo1XQpuZXdzdXJ2ZXkkYWdlcm9vdCA8LSBzcXJ0KG5ld3N1cnZleSRRMSkKaGVhZChuZXdzdXJ2ZXkpCgojIGNyZWF0aW5nIGdyb3VwcyBvdXQgb2YgYSBjb250aW51b3VzIHZhcmlhYmxlCm5ld3N1cnZleSRhZ2Vncm91cCA8LSBjdXQobmV3c3VydmV5JFExLCBicmVha3MgPSBjKDIwLCAzMCwgNDApKQpoZWFkKG5ld3N1cnZleSkKYGBgCgpVc2UgY2FzZSBmb3IgYG1lcmdlYDogeW91IGhhdmUgKnR3byogc2V0cyB5b3UgYXJlIHBsYXlpbmcgd2l0aCEgVGhpbmsgaW4gYWR2YW5jZSB3aGF0IHlvdSBuZWVkIGZvciB0aGF0IHB1cnBvc2UuLi4KCgojIyBXZSB3YW50IHN0YXRpc3RpY3MhIAoKQXJlIFBoRCBzdHVkZW50cyAqc2lnbmlmaWNhbnRseSogeW91bmdlciB0aGFuIHBvc3Rkb2NzPyBBcmUgdGhlcmUgYW55IGRpZmZlcmVuY2VzIGluIHRoZSBhZ2Ugb2YgdGhlIHRocmVlIGdyb3Vwcz8KCmBgYHtyfQpwaGRzIDwtIHN1cnZleXJiaW9jW3N1cnZleXJiaW9jJFEyID09ICJQaEQgc3R1ZGVudCIsIF0KcG9zdGRvY3MgPC0gc3VydmV5cmJpb2Nbc3VydmV5cmJpb2MkUTIgPT0gIlBvc3Rkb2MiLCBdCnQudGVzdChwaGRzJFExLCBwb3N0ZG9jcyRRMSkKYW92KGRhdGEgPSBzdXJ2ZXlyYmlvYywgUTEgfiBRMikgIyBXaGF0IGlzIG1pc3NpbmcgaGVyZT8KYGBgCgpNdWNoIG1vcmUgb24gdGhpczogaW4gdGhlIG5leHQgY291cnNlcyEKCgojIyBTaW1wbGUgeWV0IHBvd2VyZnVsIGZ1bmN0aW9ucwoKYHRhcHBseWAKCllvdSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgbWVkaWFuIGFnZSBvZiBlYWNoIGFjYWRlbWljIGdyb3VwIGluIGhlcmUKCmBgYHtyIGV2YWw9VFJVRX0KbWQgPC0gbWVkaWFuKHN1cnZleXJiaW9jJFExKQptZF9tYXN0ZXIgPC0gbWVkaWFuKHN1cnZleXJiaW9jJFExW3N1cnZleXJiaW9jJFEyID09ICJNYXN0ZXIgc3R1ZGVudC9lbHNlIl0pCm1kX3BoZCA8LSBtZWRpYW4oc3VydmV5cmJpb2MkUTFbc3VydmV5cmJpb2MkUTIgPT0gIlBoRCBzdHVkZW50Il0pCm1kX3Bvc3Rkb2NzIDwtIG1lZGlhbihzdXJ2ZXlyYmlvYyRRMVtzdXJ2ZXlyYmlvYyRRMiA9PSAiUG9zdGRvYyJdKQpjKG1kX21hc3RlciwgbWRfcGhkLCBtZF9wb3N0ZG9jcykKYGBgCgpgdGFwcGx5YCBzcGxpdHMgdGhlIGRhdGEgb2YgdGhlIGZpcnN0IHZhcmlhYmxlIG9uIHRoZSBsZXZlbHMgb2YgdGhlIHNlY29uZCB2YXJpYWJsZSwgYW5kIGFwcGxpZXMgdGhlIGZ1bmN0aW9uICgqYW55KiBmdW5jdGlvbikKCmBgYHtyIGV2YWw9VFJVRX0KdGFwcGx5KFggPSBzdXJ2ZXlyYmlvYyRRMSwgSU5ERVggPSBzdXJ2ZXlyYmlvYyRRMiwgRlVOID0gbWVkaWFuKQpgYGAKCgpgbGFwcGx5YCBhbmQgYHNhcHBseWAKCkJhY2sgdG8gb3VyIGBpcmlzYCBkYXRhc2V0CgpgYGB7ciBldmFsPVRSVUV9Cm5hbWVzKGlyaXMpCmBgYAoKV2Ugd2FudCB0aGUgYXZlcmFnZSBzZXBhbCBsZW5ndGggYW5kIHdpZHRoLCBhbmQgdGhlIHNhbWUgZm9yIHRoZSBwZXRhbHMuIFVoLCBhbmQgd2Ugd2FudCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIHRvby4KCmBgYHtyfQojIHRoZSB1bmVmZmljaWVudCB3YXk6CnNlcGxlbl9tIDwtIG1lYW4oaXJpcyRTZXBhbC5MZW5ndGgpCnNlcHdpZF9tIDwtIG1lYW4oaXJpcyRTZXBhbC5XaWR0aCkKcGV0bGVuX20gPC0gbWVhbihpcmlzJFBldGFsLkxlbmd0aCkKcGV0d2lkX20gPC0gbWVhbihpcmlzJFBldGFsLldpZHRoKQoKc2VwbGVuX20gPC0gc2QoaXJpcyRTZXBhbC5MZW5ndGgpCiMgLi4uIGFuZCBzbyBvbgpgYGAKCiRccmlnaHRhcnJvdyQgQXBwbHkgYSBGdW5jdGlvbiBvdmVyIGEgTGlzdCBvciBWZWN0b3IKCmBgYHtyfQojIHdlIHdpbGwgdXNlIGp1c3QgdGhlIGZpcnN0IGZvdXIgY29sdW1ucwpsYXBwbHkoaXJpc1ssIDE6NF0sIG1lYW4pCnNhcHBseShpcmlzWywgMTo0XSwgbWVhbikKbGFwcGx5KGlyaXNbLCAxOjRdLCBzZCkKIyAuLi4KYGBgCgpUaGUgbWFqb3IgZGlmZmVyZW5jZSBpcyBpbiB0aGUgcHJlc2VudGF0aW9uIG9mIHRoZSBvdXRwdXQKCiMjIEEgdmVyeSBnb29kIGZyaWVuZDogZ2V0IGEgYHN1bW1hcnlgIG9mIHlvdXIgZGF0YQoKKmFuZCBzb21lIG90aGVyIGZyaWVuZHMuLi4qCgpUcnkgb3V0IGBzdW1tYXJ5YCBvbiBhIGBkYXRhLmZyYW1lYAoKYGBge3J9CnN1bW1hcnkoaXJpcykKYGBgCgpBbHRlcm5hdGl2ZXMgaW4gb3RoZXIgcGFja2FnZXM6CgotIGBkZXNjcmliZSgpYCBpbiB0aGUgYEhtaXNjYCBwYWNrYWdlCi0gYHNraW0oKWAgZnJvbSBgc2tpbXJgCi0gYGNyZWF0ZV9yZXBvcnQoKWAgZnJvbSBgRGF0YSBFeHBsb3JlcmAKCgpgdGFibGVgCgpgYGB7cn0KdGFibGUoc3VydmV5cmJpb2MkUTMpCnRhYmxlKHN1cnZleXJiaW9jJFE0KQoKdGFibGUoc3VydmV5cmJpb2MkUTIsIHN1cnZleXJiaW9jJFEzKQpgYGAKCi0gd2FudCB0aGUgc3Vtcz8gVHJ5IGBhZGRtYXJnaW5zKClgCi0gbG9va2luZyBmb3IgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzPyBgcHJvcC50YWJsZSgpYAotIHNvbWV3aGF0IG5pY2VyIG91dHB1dDogYGZ0YWJsZSgpYAoKYGBge3J9CmFkZG1hcmdpbnModGFibGUoc3VydmV5cmJpb2MkUTIsIHN1cnZleXJiaW9jJFEzKSkKcHJvcC50YWJsZSh0YWJsZShzdXJ2ZXlyYmlvYyRRMiwgc3VydmV5cmJpb2MkUTMpKQpgYGAKCgpQbGVhc2UgYWx3YXlzIGRvIGNoZWNrIHRoZSBkb2NzIQoKCgojIyBFeGVyY2lzZSBzZXNzaW9uIDQKClRoZSBgTUFTU2AgcGFja2FnZSBjb250YWlucyB0aGUgZGF0YXNldCBgQ2FyczkzYCwgd2hpY2ggc3RvcmVzIHRoZSBkYXRhIG9uIDkzIG1ha2VzIG9mIGNhciBzb2xkIGluIFVTCgotIHlvdSdsbCBuZWVkIHRoZSBwYWNrYWdlICphbmQqIHRoZSBkYXRhCi0gYFR5cGVgIHNwZWNpZmllcyB0aGUgdHlwZSBvZiBtYXJrZXQgdGhlIGNhciBpcyBhaW1lZCBhdC4gRmluZCB0aGUgY2hlYXBlc3QgY2FyIGluIGVhY2ggdHlwZSwgYW5kIHRoZSBvbmUgd2l0aCB0aGUgZ3JlYXRlc3QgZnVlbCBlZmZpY2llbmN5Ci0gY29tcHV0ZSB0aGUgbWVhbiBob3JzZXBvd2VyIGZvciBlYWNoIHR5cGUKLSBjcmVhdGUgdHdvIGBkYXRhLmZyYW1lYHMsIG9uZSBmb3IgVVMgY2FycywgdGhlIG90aGVyIG9uZSB3aXRoIG5vbi1VUyBjYXJzCi0gZXhwb3J0IHRoZSBVUyBjYXJzIHRvIGEgdGV4dCBmaWxlCi0gc2F2ZSB0aGUgbm9uLVVTIGNhcnMgZGF0YSB0byBhIGJpbmFyeSBmaWxlIChgLlJEYXRhYCkKCiMjIyBFeGVyY2lzZSBTZXNzaW9uIDQgLSBTb2x1dGlvbnMKCjxkZXRhaWxzPgo8c3VtbWFyeT5DbGljayBvbiB0aGlzIHRvIGRpc3BsYXkgdGhlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKYGBge3J9CmxpYnJhcnkoTUFTUykKaGVhZChDYXJzOTMpCj9DYXJzOTMKdGFwcGx5KFggPSBDYXJzOTMkTWluLlByaWNlLCBJTkRFWCA9IENhcnM5MyRUeXBlLCBGVU4gPSBtaW4pCnRhcHBseShYID0gQ2FyczkzJEhvcnNlcG93ZXIsIElOREVYID0gQ2FyczkzJFR5cGUsIEZVTiA9IG1lYW4pCgp0YWJsZShDYXJzOTMkT3JpZ2luKQp1c19jYXJzIDwtIENhcnM5M1tDYXJzOTMkT3JpZ2luID09ICJVU0EiLCBdCm5vbnVzX2NhcnMgPC0gQ2FyczkzW0NhcnM5MyRPcmlnaW4gIT0gIlVTQSIsIF0KIyB3cml0ZS5jc3YodXNfY2FycywgZmlsZSA9ICJ1c19jYXJzLmNzdiIpCiMgc2F2ZShub251c19jYXJzLCBmaWxlID0gIm5vbnVzX2NhcnMuUkRhdGEiKQpgYGAKCjwvZGV0YWlscz4KCiMgU3RlcCA0OiBQbG90dGluZyBkYXRhIHsjc3RlcDR9CgojIyBHcmFwaGljcyBpbiBSCgogLSBwb3dlcmZ1bCBlbnZpcm9ubWVudCBmb3IgdmlzdWFsaXppbmcgc2NpZW50aWZpYyBkYXRhCiAtIGludGVncmF0ZWQgZ3JhcGhpY3MgKipBTkQqKiBzdGF0aXN0aWNzCiAtIHB1YmxpY2F0aW9uLXJlYWR5IHF1YWxpdHkKIC0gZnVsbHkgcHJvZ3JhbW1hYmxlLCBoaWdobHkgcmVwcm9kdWNpYmxlCgpNYW55IHdheXMgZm9yIHRoZSBzYW1lIHRhc2s6CgotIGJhc2UgZ3JhcGhpY3MgKGBwbG90YCkKLSBgZ2dwbG90MmAKLSBgbGF0dGljZWAKLSBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucyBzdWNoIGFzIGBwbG90bHlgLCBgZ2d2aXNgIG9yIG90aGVyIGxpYnJhcmllcwoKV2h5IGJvdGhlciBwbG90dGluZyBhdCBhbGw/CgotIGZhY2lsaXRhdGUgY29tcGFyaXNvbnMKLSBpZGVudGlmeSB0cmVuZHMKLSBnZW5lcmF0ZSBoeXBvdGhlc2VzCgoKCiMjIFRoZSBgcGxvdGAgZnVuY3Rpb24KCkZpcnN0IHRoaW5nOiB0YWtlIGEgbG9vayBhdCB0aGUgb3ZlcnZpZXcgZG9jdW1lbnRhdGlvbiBvZiBgcGxvdGAKCmBgYHtyfQo/cGxvdApgYGAKCldlIHdpbGwgc2VlCgotIHNjYXR0ZXIgcGxvdHMKLSBib3hwbG90cwotIGJhcnBsb3RzCi0gaGlzdG9ncmFtcwoKCiMjIGBwbG90YCBwYXJhbWV0ZXJzIAoKUmVxdWlyZWQ6CgotIHggdmFyaWFibGUKLSB5IHZhcmlhYmxlCgpPdGhlciBvcHRpb25zCgotIHRpdGxlIHdpdGggYG1haW5gCi0gYXhlcyBsYWJlbHMgd2l0aCBgeGxhYmAgYW5kIGB5bGFiYAotIGF4ZXMgbGltaXRzIHdpdGggYHhsaW1gIGFuZCBgeWxpbWAKLSBzeW1ib2xzLCBjb2xvcnMgYW5kIHNpemVzOiBgcGNoYCwgYGNvbGAgYW5kIGBjZXhgIC0gYXMgYXRvbWljIGVsZW1lbnRzIG9yIGFzIHZlY3RvcnMKCgojIyBHZXQgdG8ga25vdyB0aGUgZGF0YTogYG1wZ2AKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpICMgdGhpcyBpcyB1c2VmdWwgcGVyIHNlLCBhbmQgY29udGFpbnMgdGhlIGRhdGFzZXQgd2Ugd2lsbCBiZSB1c2luZwo/bXBnCmBgYAoKICAgIFRoaXMgZGF0YXNldCBjb250YWlucyBhIHN1YnNldCBvZiB0aGUgZnVlbCBlY29ub215IGRhdGEgdGhhdCB0aGUgRVBBIG1ha2VzIGF2YWlsYWJsZSBvbiBodHRwOi8vZnVlbGVjb25vbXkuZ292CgpgYGB7cn0KIyB3b3JrcyBvbiBSU3R1ZGlvCiMgVmlldyhtcGcpCiMgb3RoZXJ3aXNlIHN0aWNrIHRvIHRoZSBjbGFzc2ljCnN0cihtcGcpCmBgYAoKTWFrZSBhIGd1ZXNzOiB3aGF0IGRvIHlvdSBleHBlY3QgdG8gc2VlIGJldHdlZW4gZnVlbCBjb25zdW1wdGlvbiBhbmQgZW5naW5lIHNpemU/CgojIyBTY2F0dGVyIHBsb3RzCgpgYGB7ciBldmFsPVRSVUV9CnBsb3QobXBnJGRpc3BsLCBtcGckY3R5KQpgYGAKCkJvbnVzOiB3aGF0IGlzIHRoZSBgY29yYHJlbGF0aW9uPwoKYGBge3J9CmNvcihtcGckZGlzcGwsIG1wZyRjdHkpCmNvcihtcGckZGlzcGwsIG1wZyRjdHksIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmBgYAoKCiMjIyBDYW4gd2UgZG8gbW9yZT8KCmBgYHtyIGV2YWw9VFJVRX0KbXBnJG15Z3JvdXAgPC0gYXMubnVtZXJpYyhmYWN0b3IobXBnJGNsYXNzKSkKcGxvdChtcGckZGlzcGwsIG1wZyRjdHksCiAgY29sID0gbXBnJG15Z3JvdXAKKQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihtcGckY2xhc3MpKSwgY29sID0gbGV2ZWxzKGZhY3RvcihtcGckbXlncm91cCkpLCBwY2ggPSAxKQpgYGAKCgpgYGB7ciBldmFsPVRSVUV9CnBsb3QobXBnJGRpc3BsLCBtcGckY3R5LAogIHBjaCA9IGFzLm51bWVyaWMoZmFjdG9yKChtcGckY2xhc3MpKSkKKQpgYGAKClRoaXMgc2hvd3Mgd2UgaGF2ZSBxdWl0ZSBzb21lIG92ZXJsYXAgb2YgcG9pbnRzLiBXaGF0IGNhbiB3ZSBkbz8KCkFkZGluZyBzb21lIGppdHRlci4uLgoKYGBge3IgZXZhbD1UUlVFfQpwbG90KAogIHggPSBtcGckZGlzcGwgKyBybm9ybShucm93KG1wZyksIG1lYW4gPSAwLCBzZCA9IDAuMDEpLAogIHkgPSBtcGckY3R5ICsgcm5vcm0ocm5vcm0obnJvdyhtcGcpLCBtZWFuID0gMCwgc2QgPSAwLjAxKSksCiAgY29sID0gbXBnJG15Z3JvdXAsCiAgbWFpbiA9ICJub3cgd2l0aCBqaXR0ZXIhIgopCmBgYAoKCkFkZGluZyBhIHNtb290aGluZyBsaW5lCgpUcnlpbmcgdG8gc2VlIGEgcGF0dGVybj8gQWRkIGEgc21vb3RoaW5nIGN1cnZlLgoKVGhpcyBvbmUgaXMgd3JvbmcgLSBtaXNzaW5nIHRoZSByZW9yZGVyaW5nIG9mIHBvaW50cwoKYGBge3IgZXZhbD1UUlVFfQpwbG90KG1wZyRkaXNwbCwgbXBnJGN0eSwgY29sID0gbXBnJG15Z3JvdXApCm15bG9lc3MgPC0gbG9lc3MoY3R5IH4gZGlzcGwsIGRhdGEgPSBtcGcpCm15Zml0IDwtIGZpdHRlZChteWxvZXNzKQpsaW5lcyhtcGckZGlzcGwsIG15Zml0KQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihtcGckY2xhc3MpKSwgY29sID0gbGV2ZWxzKGZhY3RvcihtcGckbXlncm91cCkpLCBwY2ggPSAxKQpgYGAKClRoaXMgb25lIGlzIGNvcnJlY3QhCgpgYGB7ciBldmFsPVRSVUV9CnBsb3QobXBnJGRpc3BsLCBtcGckY3R5LCBjb2wgPSBtcGckbXlncm91cCkKbXlsb2VzcyA8LSBsb2VzcyhjdHkgfiBkaXNwbCwgZGF0YSA9IG1wZykKbXlmaXQgPC0gZml0dGVkKG15bG9lc3MpCm15b3JkIDwtIG9yZGVyKG1wZyRkaXNwbCkKbGluZXMobXBnJGRpc3BsW215b3JkXSwgbXlmaXRbbXlvcmRdKQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihtcGckY2xhc3MpKSwgY29sID0gbGV2ZWxzKGZhY3RvcihtcGckbXlncm91cCkpLCBwY2ggPSAxKQpgYGAKCmBsaW5lc2AgY2FuIGFkZCAoYWxtb3N0KSBhbnl0aGluZyAoYW55IGxpbmUpLiAKCmBwb2ludHNgIHdvcmtzIGluIGEgc2ltaWxhciB3YXkgdG8gc3VwZXJpbXBvc2UsIHdlbGwsIHBvaW50cwoKCiMjIEJhciBjaGFydHMKCmBgYHtyfQo/YmFycGxvdApgYGAKCmBgYHtyIGV2YWw9VFJVRX0KYWNhZGVtaWFfbGV2ZWxzIDwtIHRhYmxlKHN1cnZleXJiaW9jJFEyKQpiYXJwbG90KGFjYWRlbWlhX2xldmVscykKYGBgCgoKIyMgQm94cGxvdHMKCkhvdyBpcyB0aGUgYWdlIGRpc3RyaWJ1dGVkIGFjcm9zcyBhY2FkZW1pYyBsZXZlbHM/IENoZWNrIHRoZSBoZWxwIG9mIGBib3hwbG90YAoKLSBBIGZvcm11bGEgaXMgcmVxdWlyZWQhCi0gRG9uJ3Qgd29ycnksIGl0J3Mgbm90aGluZyBidXQgeW91ciBgeX54YCB2YXJpYWJsZXMgLSBvaywgaXQgY2FuIGdldCBtb3JlIGNvbXBsaWNhdGVkCiAgICAKYGBge3IgZXZhbD1UUlVFfQpib3hwbG90KFExIH4gUTIsCiAgZGF0YSA9IHN1cnZleXJiaW9jCikKYGBgCgpTcGxpdHRpbmcgb24gbW9yZSBmYWN0b3JzCgpgYGB7ciBldmFsID0gVFJVRX0KYm94cGxvdChRMSB+IFEyICsgUTMsCiAgZGF0YSA9IHN1cnZleXJiaW9jCikKYGBgCgpNYWtpbmcgaXQgbW9yZSByZWFkYWJsZS4uLgoKYGBge3IgZXZhbCA9IFRSVUV9CmJveHBsb3QoUTEgfiBRMiArIFEzLAogIGRhdGEgPSBzdXJ2ZXlyYmlvYywKICBsYXMgPSAyCikKYGBgCgpDaGFuZ2luZyB0aGUgYHBhcmBhbWV0ZXJzIGFsbG93cyB5b3UgdG8gY29udHJvbCBtYW55IGFzcGVjdHMgb24gcGxvdCBhcHBlYXJhbmNlCmBwYXJgIGlzIHlvdXIgYmVzdCBmcmllbmQgLSBhbmQgZW5lbXkgKHNlZSBgP3BhcmApCgpgYGB7ciBldmFsPVRSVUV9CnBhcihtYXIgPSBjKDE1LCAzLCAyLCAyKSkKYm94cGxvdChRMSB+IFEyICsgUTMsIGRhdGEgPSBzdXJ2ZXlyYmlvYywgbGFzID0gMikKYGBgCgpgcGFyKCAuLi4gKWAgaGFzIG1hbnkgYXJndW1lbnRzOyBoZXJlLCB0aGUgdXNlZnVsL21vc3QgdXNlZCBvbmVzCgotIGBtYXJgIGZvciBoYW5kbGluZyB0aGUgbWFyZ2lucwotIGBjZXhgLCBgY29sYCwgYHBjaGAgYW5kIGNvLiBhcmUgYWxsIHBhcmFtZXRlcnMgb2YgYHBhcmAKLSBgbGFzYCB0byBjaGFuZ2UgdGhlIHN0eWxlIG9mIHRoZSBheGlzIGxhYmVscwotIGBtZnJvd2AgdG8gZHJhdyBhbiBhcnJheSBvZiBmaWd1cmVzCgoKIyMgSGlzdG9ncmFtcwoKYGBge3IgZXZhbD1UUlVFfQpoaXN0KHN1cnZleXJiaW9jJFExLCBicmVha3MgPSA4KQpgYGAKCgojIyBNb3JlIGhpc3RvZ3JhbXMhCgpgYGB7cn0KaGlzdChtcGckY3R5LCBicmVha3MgPSAxMCkKaGlzdChtcGckY3R5LCBicmVha3MgPSAxMCwgY29sID0gInN0ZWVsYmx1ZSIpCmhpc3QobXBnJGN0eSwgYnJlYWtzID0gMTAsIGNvbCA9ICJzdGVlbGJsdWUiLCBib3JkZXIgPSAiZ3JheSIpCmhpc3QobXBnJGN0eSwgYnJlYWtzID0gMTAsIGNvbCA9ICJzdGVlbGJsdWUiLCBib3JkZXIgPSAiZ3JheSIsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIG1pbGVzL2dhbGxvbiBjb25zdW1wdGlvbiBpbiBjaXR5IHRyYWZmaWMiKQpgYGAKCgojIyBIb3cgdG8gZG8gbmljZSBwaWUgY2hhcnRzCgpET04nVC4KCklmIHlvdSAqKnJlYWxseSoqIG5lZWQgdG8gZG8gaXQuLi4KCmBgYHtyfQo/cGllCmV4YW1wbGUocGllKSAjIGV4cGVjaWFsbHkgdGhlIGxhc3Qgb25lCmBgYAoKYGBge3J9CnBpZShjKDIwLCA4MCksCiAgaW5pdC5hbmdsZSA9IC00MCwKICBjb2wgPSBjKCJ3aGl0ZSIsICJ5ZWxsb3ciKSwKICBsYWJlbCA9IGMoIm5vIHBhY21hbiIsICJwYWNtYW4iKSwKICBib3JkZXIgPSAibGlnaHRncmV5IgopCmBgYAoKLi4uIG9yIHN3aXRjaCBmcm9tIHBpZSB0byB3YWZmbGUgKHNlcmlvdXNseSkKCiMjIEhvdyB0byBkbyAzRCBleHBsb2RlZCBwaWUgY2hhcnRzCgoqKkRPTidUKiouIEFuZCB0aGlzIHRpbWUgSSBtZWFuIGl0Cgoqc2FkbHkgZW5vdWdoIHRoZXJlIHdvdWxkIGJlIHBhY2thZ2VzIGZvciB0aGlzLCB0b28qCgoKCiMjIEV4dHJhOiAqZHluYW1pdGUqIHBsb3RzCgphLmsuYS4gV2h5IGlzIHRoaXMgYmFkPwoKYGBge3IgZXZhbD1UUlVFfQphZ2VfYnlfZ3JvdXAgPC0gdGFwcGx5KHN1cnZleXJiaW9jJFExLCBzdXJ2ZXlyYmlvYyRRMiwgbWVhbikKc2RfYnlfZ3JvdXAgPC0gdGFwcGx5KHN1cnZleXJiaW9jJFExLCBzdXJ2ZXlyYmlvYyRRMiwgc2QpCm15YmFyIDwtIGJhcnBsb3QoYWdlX2J5X2dyb3VwLCBjb2wgPSBjKCJraGFraSIsICJzYWxtb24iLCAiZmlyZWJyaWNrIiksIHlsaW0gPSBjKDAsIG1heChhZ2VfYnlfZ3JvdXApICsgNSkpCiMgbXliYXIsIGluc3BlY3QgaXQKYXJyb3dzKG15YmFyLCBhZ2VfYnlfZ3JvdXAsIG15YmFyLCAoYWdlX2J5X2dyb3VwICsgc2RfYnlfZ3JvdXApLCBsZW5ndGggPSAwLjE1LCBhbmdsZSA9IDkwKQpgYGAKCgpEeW5hbWl0ZSBwbG90cyBWUyBib3hwbG90cwoKYGBge3IgZXZhbD1UUlVFfQpib3hwbG90KFExIH4gUTIsCiAgZGF0YSA9IHN1cnZleXJiaW9jCikKYGBgCgpNZWRpYW4gVlMgZGlzdHJpYnV0aW9uIFZTIGFjdHVhbCBwb2ludHMuLi4gV2hhdCBkbyB5b3UgcmVhbGx5IHdhbnQgdG8gc2hvdz8KCgojIyBXaGF0IGNhbiB5b3UgZG8gbW9yZSB3aXRoIHlvdXIgcGxvdD8KCi0gY2hhbmdlIHRoZSBwb2ludHMgdHlwZSAtIHNlZSBgdHlwZWAgaW4gYD9wbG90YAotIHVzZSBsb2cgc2NhbGVzIC0gc2VlIGBsb2dgCi0gYW5ub3RhdGUgKHNvbWUgb2YpIHRoZSBwb2ludHMgLSB3aXRoIGB0ZXh0YAotIGNoYW5nZSBmb250IHNpemVzLCBzdHlsZXMgYW5kIHNvIG9uCi0gdXNlIHNwZWNpYWwgY2hhcmFjdGVycyB3aXRoIGBleHByZXNzaW9uYAotIHNhdmUgdGhlIHBsb3QKICAtIHVzZSB0aGUgcG9pbnQtYW5kLWNsaWNrIGludGVyZmFjZSBpbiBSU3R1ZGlvCiAgLSBjb2RlIGl0CiAgICAKIyMjIFNhdmluZyB5b3VyIHBsb3RzCgpHZW5lcmFsIGNvZGUgc3RydWN0dXJlIGZvciB0aGlzCgpgYGAKb3BlbmRldmljZSgpCi4uLgpjb2RlIGZvciB0aGUgcGxvdAouLi4KY2xvc2VkZXZpY2UoKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CnBkZigibXlmaWxlbmFtZS5wZGYiKQojIHNlZSBhbHNvIGFsdGVybmF0aXZlczoKIyMgcG5nKCkKIyMganBlZygpCnBsb3QobXBnJGRpc3BsLCBtcGckY3R5LAogIGNvbCA9IG1wZyRteWdyb3VwCikKZGV2Lm9mZigpCmBgYAoKCgojIyBQZXRhbHMgYW5kIHNlcGFscwoKPGltZyBzcmM9ImltYWdlcy9wZXRhbC1zZXBhbC5qcGciIGFsdD0iIiBoZWlnaHQ9IjYwMCIvPgoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiA1CgpCYWNrIHRvIHRoZSBgaXJpc2AuIFRocmVlIHNwZWNpZXMgYXJlIHRoZXJlLiBFeHBsb3JlIHRoZSBkYXRhc2V0IGluIHRoZSBmb2xsb3dpbmcgd2F5czoKCi0gZHJhdyBhIGhpc3RvZ3JhbSBvZiB0aGUgcGV0YWwgbGVuZ3RoLiBXaGF0IGRvIHlvdSBzZWU/Ci0gcGxvdCBzZXBhbCBsZW5ndGggdmVyc3VzIHBldGFsIGxlbmd0aC4gQWRkIGRpZmZlcmVudCBjb2xvcnMgdG8gaGlnaGxpZ2h0IHRoZSBzcGVjaWVzCi0gZG8gdGhlIHNhbWUgZm9yIHNlcGFsIHdpZHRoIGFuZCBzZXBhbCBsZW5ndGgsIGFuZCB0aGlzIHRpbWUgdXNlIGEgZGlmZmVyZW50IHN5bWJvbCBmb3IgdGhlIHNwZWNpZXMuIEFkZCBhIGxlZ2VuZCBhbmQgYSB0aXRsZSBpZiB5b3Ugd2FudAotIChoYXJkZXIpIGNhbGN1bGF0ZSB0aGUgbWVhbiB2YWx1ZXMgb2YgZWFjaCBmZWF0dXJlIGZvciBlYWNoIHNwZWNpZXMsIG9yZ2FuaXppbmcgaXQgaW4gYSBtYXRyaXggd2hlcmUgdGhlIHJvd3MgYXJlIHRoZSBzcGVjaWVzIG5hbWVzLiBHZW5lcmF0ZSBhIHN0YWNrZWQgYmFyIHBsb3Qgd2l0aCBpdCwgYW5kIGFub3RoZXIgb25lIHdoZXJlIHRoZSBiYXJzIGFyZSBhcnJhbmdlZCBob3Jpem9udGFsbHkKCi0gZmVlbCBmcmVlIHRvIGdvIGJhY2sgdG8gdGhlIHN1cnZleSBkYXRhIGFuZCBleHBsb3JlIGl0IGZ1cnRoZXIhCgojIyMgRXhlcmNpc2UgU2Vzc2lvbiA1IC0gU29sdXRpb25zCgo8ZGV0YWlscz4KPHN1bW1hcnk+Q2xpY2sgb24gdGhpcyB0byBkaXNwbGF5IHRoZSBzb2x1dGlvbjwvc3VtbWFyeT4KCmBgYHtyfQpoaXN0KGlyaXMkUGV0YWwuTGVuZ3RoKQpwbG90KGlyaXMkU2VwYWwuTGVuZ3RoLCBpcmlzJFBldGFsLkxlbmd0aCkKcGxvdChpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRQZXRhbC5MZW5ndGgsIGNvbCA9IGlyaXMkU3BlY2llcykKcGxvdChpcmlzJFNlcGFsLldpZHRoLCBpcmlzJFNlcGFsLkxlbmd0aCwgcGNoID0gYXMubnVtZXJpYyhmYWN0b3IoaXJpcyRTcGVjaWVzKSkpCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBsZXZlbHMoZmFjdG9yKGlyaXMkU3BlY2llcykpLCBwY2ggPSB1bmlxdWUoZmFjdG9yKGlyaXMkU3BlY2llcykpKQoKc2xfbWVhbnMgPC0gdGFwcGx5KGlyaXMkU2VwYWwuTGVuZ3RoLCBpcmlzJFNwZWNpZXMsIG1lYW4pCnBsX21lYW5zIDwtIHRhcHBseShpcmlzJFBldGFsLkxlbmd0aCwgaXJpcyRTcGVjaWVzLCBtZWFuKQpzd19tZWFucyA8LSB0YXBwbHkoaXJpcyRTZXBhbC5XaWR0aCwgaXJpcyRTcGVjaWVzLCBtZWFuKQpwd19tZWFucyA8LSB0YXBwbHkoaXJpcyRQZXRhbC5XaWR0aCwgaXJpcyRTcGVjaWVzLCBtZWFuKQpteW1hdCA8LSBjYmluZChzbF9tZWFucywgcGxfbWVhbnMsIHN3X21lYW5zLCBwd19tZWFucykKYmFycGxvdChteW1hdCwgbGVnZW5kLnRleHQgPSB1bmlxdWUoaXJpcyRTcGVjaWVzKSkKYmFycGxvdChteW1hdCwgYmVzaWRlID0gVFJVRSwgbGVnZW5kLnRleHQgPSB1bmlxdWUoaXJpcyRTcGVjaWVzKSkKYGBgCgo8L2RldGFpbHM+CgojIyBTb21ldGhpbmcgY29vbCB0byBoYXZlIGFuIG92ZXJ2aWV3Li4uCgpgYGB7ciBldmFsPVRSVUV9CnBhaXJzKGlyaXNbLCAxOjRdLCBjb2wgPSBpcmlzJFNwZWNpZXMpCmBgYAoKWW91IGNhbiB1c2UgdGhlIHBhbmVscyBldmVuIG1vcmUgY2xldmVybHksIGNoZWNrIHRoZSBoZWxwIG9mIGBwYWlyc2AhCgpUaGlzIGlzIGEgY29sbGVjdGlvbiBvbiBncmFwaHMgaW4gUiAtIHdpdGggdGhlIHVuZGVybHlpbmcgY29kZSB0b28uCgpodHRwOi8vc2hpbnkuc3RhdC51YmMuY2Evci1ncmFwaC1jYXRhbG9nLwoKCiMjIFRoZSBgZ2FwbWluZGVyYCBwcm9qZWN0CgpTaXQgYW5kIHJlbGF4L2Vuam95Li4uCgpodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PWhWaW1Wemd0RDZ3Cgo8IS0tIDxpZnJhbWUgd2lkdGg9Ijg1NCIgaGVpZ2h0PSI0ODAiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvaFZpbVZ6Z3RENnc/dD0yNSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+IC0tPgoKIyMgTWVldCBgZ2dwbG90MmAKCkJ1dCBmaXJzdCwgbWVldCB0aGUgYGdhcG1pbmRlcmAgZGF0YSAKCmBgYHtyfQpsaWJyYXJ5KGdhcG1pbmRlcikKaGVhZChnYXBtaW5kZXIpCmhlYWQoY291bnRyeV9jb2xvcnMpCmhlYWQoY29udGluZW50X2NvbG9ycykKYGBgCgpWYXJpYWJsZXM6IAoKLSBjb3VudHJ5IAkKLSBjb250aW5lbnQgCQotIHllYXIgCQotIGxpZmVFeHAsIGxpZmUgZXhwZWN0YW5jeSBhdCBiaXJ0aAotIHBvcCwgdG90YWwgcG9wdWxhdGlvbiAKLSBnZHBQZXJjYXAsIHBlci1jYXBpdGEgR0RQCgo8IS0tIDxpbWcgc3JjPSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vamVubnliYy9nYXBtaW5kZXIvbWFzdGVyL2RhdGEtcmF3L2dhcG1pbmRlci1jb2xvci1zY2hlbWUtZ2dwbG90Mi5wbmciIGFsdD0iIiBoZWlnaHQ9IjQwMCIvPiAtLT4KCjwhLS0gIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qZW5ueWJjL2dhcG1pbmRlci9tYXN0ZXIvZGF0YS1yYXcvZ2FwbWluZGVyLWNvbG9yLXNjaGVtZS1nZ3Bsb3QyLnBuZykgLS0+CgoKIyMgVGhlIGBnZ3Bsb3QyYCBwaGlsb3NvcGh5CgpgZ2dgIHN0YW5kcyBmb3IgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MKCi0geW91IHByb3ZpZGUgdGhlIGBkYXRhYAotIHlvdSBtYXAgdGhlIGRhdGEgdG8gYGFlc2B0aGV0aWNzIChzaGFwZSwgc2l6ZSwgY29sb3VyKQotIHlvdSBhZGQgYGdlb21gcyB0byBzcGVjaWZ5IGhvdyB5b3Ugd2FudCB0byBoYXZlIHRoZSBkYXRhIHBsb3R0ZWQKLSB5b3UgY2FuIGhhdmUgYHN0YXRgaXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMKLSBgZmFjZXRgcyBhbGxvdyB5b3UgdG8gZG8gcXVpY2sgZWxlZ2FudCBtdWx0aSBwbG90cwoKSXQgY2FuIGNvbWUgYWNyb3NzIHNvbWV3aGF0IGhhcmRlciBzaW5jZQoKLSBkYXRhIG5lZWQgdG8gYmUgdGlkeSAtIG9uZSBvYnNlcnZhdGlvbiBwZXIgcm93Ci0gcmVxdWlyZXMgYW4gZXh0cmEgc3RlcCBmb3IgYWJzdHJhY3Rpb24KCnlldCwgaXQgbWFrZXMgdGhlIHdob2xlIHByb2Nlc3Mgb2YgInRoaW5raW5nIGRhdGEiIG1vcmUgbmF0dXJhbC4KCgojIyMgQSBxdWljayBkaXZlIGludG8gdGhlIG1hbnkgb3B0aW9ucwoKCmBgYHtyIGV2YWwgPSBUUlVFfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCldlIGNhbiBzdG9yZSBgZ2dwbG90YCBwbG90IG9iamVjdHMgaW50byBhIHZhcmlhYmxlIC0gYW5kIGJ1aWxkIHVwb24gdGhhdCBsYXRlciAKCmBgYHtyIGV2YWw9VFJVRX0KcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKQpwICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeF9sb2cxMCgpCnAgPC0gcCArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChjb2xvciA9ICJzdGVlbGJsdWUiLCBwY2ggPSAxOSwgc2l6ZSA9IDgsIGFscGhhID0gMSAvIDQpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjb250aW5lbnQpKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoYWVzKGNvbCA9IGNvbnRpbmVudCksIHNpemUgPSA0KQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoYWVzKGNvbCA9IGNvbnRpbmVudCwgc2l6ZSA9IHBvcCkpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sID0gY29udGluZW50LCBzaXplID0gcG9wKSkgKyBnZW9tX3Ntb290aCgpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpuaWNlb25lIDwtIHAgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSBjb250aW5lbnQsIHNpemUgPSBwb3ApKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbCA9IGNvbnRpbmVudCksIHNlID0gRkFMU0UpCm5pY2VvbmUKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSBjb250aW5lbnQsIHNpemUgPSBwb3ApKSArCiAgZ2VvbV9zbW9vdGgobHdkID0gMiwgc2UgPSBGQUxTRSwgbWV0aG9kID0gImxtIiwgY29sID0gInJlZCIpCmBgYAoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9wb2ludChhZXMoY29sID0gY29udGluZW50LCBzaXplID0gcG9wKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2wgPSBjb250aW5lbnQpLCBsd2QgPSAyLCBzZSA9IEZBTFNFLCBtZXRob2QgPSAibG0iKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofmNvbnRpbmVudCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSBjb250aW5lbnQpKSArIGZhY2V0X3dyYXAofmNvbnRpbmVudCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSBjb250aW5lbnQpKSArIGdlb21fc21vb3RoKCkgKyBmYWNldF93cmFwKH5jb250aW5lbnQpCmBgYAoKIyMjIFNhdmluZyB0aGUgcGxvdHMKCkJlY29tZXMgZGUgZmFjdG8gZWFzaWVyIC0gd29ya3Mgb24gInBsb3Qgb2JqZWN0cyIuCgpgYGB7ciBldmFsPUZBTFNFfQpnZ3NhdmUoZmlsZSA9ICJteXBsb3QucG5nIikKYGBgCgojIyMgTGluZSBwbG90cwoKYGBge3IgZXZhbD1UUlVFfQpnZ3Bsb3QoCiAgZ2FwbWluZGVyLAogIGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHAsIGdyb3VwID0gY291bnRyeSwgY29sb3IgPSBjb3VudHJ5KQopICsKICBnZW9tX2xpbmUobHdkID0gMSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb3VudHJ5X2NvbG9ycykgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjEpKSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CmJwIDwtIGdncGxvdCgKICBnYXBtaW5kZXIsCiAgYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCwgZ3JvdXAgPSBjb3VudHJ5LCBjb2xvciA9IGNvdW50cnkpCikgKwogIGdlb21fbGluZShsd2QgPSAxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvdW50cnlfY29sb3JzKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMSkpKQpicApgYGAKCk9uZSBzdGVwIGF3YXkgZnJvbSBtYWtpbmcgdGhpbmdzIGludGVyYWN0aXZlIC0gbW9yZSBpbnRlcmVzdGluZz8gbW9yZSBhY2Nlc3NpYmxlPyBtb3JlIGV4cGxvcmFibGU/CgpgYGB7ciBldmFsPVRSVUUsbWVzc2FnZT1GQUxTRX0KcGxvdGx5OjpnZ3Bsb3RseShicCkKYGBgCgoKIyMjIEJveHBsb3RzCgoKYGBge3IgZXZhbD1UUlVFfQojIG5vdyBpdCBpcyBhIGNhdGVnb3JpY2FsIHggVlMgY29udGludW91cyB5CnAgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBjb250aW5lbnQsIHkgPSBsaWZlRXhwKSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX3BvaW50KGFscGhhID0gMSAvIDQpCmBgYAoKSXQgaXMgc28gZWFzeSB0byBlc2NhcGUgKmR5bmFtaXRlKiBwbG90cyEKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21faml0dGVyKCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2ppdHRlcihhZXMoY29sID0gY29udGluZW50KSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAxIC8gMikKYGBgCgojIyMgSGlzdG9ncmFtcwoKYGBge3IgZXZhbD1UUlVFfQpwIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyhsaWZlRXhwKSkKcCArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpCmBgYAoKU3RhY2tlZCBoaXN0b2dyYW0gYXJlIG11Y2ggZWFzaWVyIGluIHRoaXMgZnJhbWV3b3JrCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoY29sb3IgPSBjb250aW5lbnQpKQpgYGAKCmBgYHtyIGV2YWw9VFJVRX0KcCArIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY29udGluZW50KSkKYGBgCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbCA9IGNvbnRpbmVudCksIHBvc2l0aW9uID0gImlkZW50aXR5IikKYGBgCgouLi4gYW5kIHNvIGlzIHRoZSBzdXBlcmltcG9zaW5nIG9mIG1vcmUgdGhhbiBvbmUgZGlzdHJpYnV0aW9uCgpgYGB7ciBldmFsPVRSVUV9CnAgKyBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbCA9IGNvbnRpbmVudCksIHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjQpCmBgYAoKU2ltaWxhciB0byBoaXN0b2dyYW0sIHlvdSBjYW4gdXNlIGFsc28gZGVuc2l0eSBwbG90cwoKYGBge3IgZXZhbD1UUlVFfQpwICsgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsID0gY29udGluZW50KSwgYWxwaGEgPSAxIC8gNCkKYGBgCgojIyMgVGhlbWVzOiBhIHF1aWNrIHdheSB0byBwdXQgYSBuZXcgc2hpcnQgb24KCmBgYHtyIGV2YWw9VFJVRX0KbmljZW9uZSArIHRoZW1lX2J3KCkKYGBgCgpgYGB7ciBldmFsPVRSVUV9Cm5pY2VvbmUgKyB0aGVtZV92b2lkKCkKYGBgCgpJZiB5b3UgcmVhbGx5IHJlYWxseSByZWFsbHkgaGF2ZSB0by4uLgoKYGBge3IgZXZhbD1UUlVFfQpsaWJyYXJ5KCJnZ3RoZW1lcyIpCm5pY2VvbmUgKyB0aGVtZV9leGNlbCgpICsgc2NhbGVfY29sb3JfZXhjZWwoKQpgYGAKCiMjIEV4ZXJjaXNlIHNlc3Npb24gNiAtIEhvbWV3b3JrIGlmIHlvdSB3YW50CgotIHRyeSB0byByZWNyZWF0ZSB0aGUgcGxvdHMgeW91IGRpZCB3aXRoIGJhc2UgZ3JhcGhpY3MsIHRoaXMgdGltZSB1c2luZyBgZ2dwbG90MmAgCgotIHBpY2sgYSBuaWNlIHBsb3QgeW91IHdvdWxkIGxpa2UgdG8gaGF2ZSBpbiB5b3VyIG5leHQgbWFudXNjcmlwdDogY2FuIHlvdSB0aGluayBvZiB3aGF0IHlvdSBuZWVkIHRvIGRvIGl0PyBJIGFtIHRhbGtpbmcgb2YgCiAgICAtIHdoYXQgZGF0YSB0eXBlPwogICAgLSB3aGF0IHRyYW5zZm9ybWF0aW9ucz8KICAgIC0gd2hhdCBwbG90IHR5cGUvbGF5ZXI/CgoKPCEtLSAjIHRhYnVsYXIgZGF0YSBhbmFseXNpcyAtIG9uIHRoZSBSTkEgZGF0YXNldD8gLS0+Cgo8IS0tICMgZGF0YSB2aXN1YWxpemF0aW9uIC0tPgoKPCEtLSAjIGpvaW5pbmcgdGFibGVzICg/KSAtLT4KCjxkZXRhaWxzPgo8c3VtbWFyeT5DbGljayBvbiB0aGlzIHRvIGRpc3BsYXkgdGhlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKCjwvZGV0YWlscz4KCgojIEJvbnVzIFN0ZXA6IHJlcHJvZHVjaWJsZSByZXBvcnRzIHsjcmVwcm9yZXBvcnRzfQoKT3VyIGFpbXM6IAoKLSBVbmRlcnN0YW5kIHdoYXQgUiBNYXJrZG93biBpcyBhbmQgd2h5IHlvdSBzaG91bGQgdXNlIGl0Ci0gTGVhcm4gaG93IHRvIGNvbnN0cnVjdCBhbiBSIE1hcmtkb3duIGZpbGUKLSBFeHBvcnQgYW4gUiBNYXJrZG93biBmaWxlIGludG8gbWFueSBmaWxlIGZvcm1hdHMKLSAkXHJpZ2h0YXJyb3ckIFlvdSBhcmUgYWxsIHNldCB0byB1c2UgUm1kIHRvIGRvY3VtZW50IGFueSBvZiB5b3VyIGFuYWx5c2VzIQoKIyMgUmVwcm9kdWNpYmxlIHJlcG9ydHMgd2l0aCBSIE1hcmtkb3duCgpSIE1hcmtkb3duIGFsbG93cyB5b3UgdG8gY3JlYXRlIGRvY3VtZW50cyB0aGF0IHNlcnZlIGFzIGEgbmVhdCByZWNvcmQgb2YgeW91ciBhbmFseXNpcy4gCgpXaHk/CgotIHdlIHdhbnQgb3RoZXIgcmVzZWFyY2hlcnMgdG8gZWFzaWx5IHVuZGVyc3RhbmQgd2hhdCB3ZSBkaWQgaW4gb3VyIGFuYWx5c2lzLCBvdGhlcndpc2Ugbm9ib2R5IGNhbiBiZSBjZXJ0YWluIHRoYXQgeW91IGFuYWx5c2VkIHlvdXIgZGF0YSBwcm9wZXJseSAoeWF5LCByZXByb2R1Y2libGUgcmVzZWFyY2ghKQotIGNyZWF0ZSBhbiBSIG1hcmtkb3duIGRvY3VtZW50IGFzIGFuIGFwcGVuZGl4IHRvIGEgcGFwZXIgb3IgcHJvamVjdCBhc3NpZ25tZW50LCB1cGxvYWQgaXQgdG8gYW4gb25saW5lIHJlcG9zaXRvcnkgc3VjaCBhcyBHaXRodWIsIG9yIHNpbXBseSB0byBrZWVwIGFzIGEgcGVyc29uYWwgcmVjb3JkICgqZnV0dXJlIHlvdSogd2lsbCB0aGFuayAqcHJlc2VudCB5b3UqIGZvciB0aGlzKQoKVGhlIGtleSBwb2ludCBpcy4uLgoKUiBNYXJrZG93biBkb2N1bWVudHMgcHJlc2VudCB5b3VyIGNvZGUgYWxvbmdzaWRlIGl0cyBvdXRwdXQgKGdyYXBocywgdGFibGVzLCBldGMuKSB3aXRoIGNvbnZlbnRpb25hbCB0ZXh0IHRvIGV4cGxhaW4gaXQsIGEgYml0IGxpa2UgYSBub3RlYm9vay4gVG8gZG8gdGhpcywgUiBNYXJrZG93biB1c2VzICoqbWFya2Rvd24qKiBzeW50YXguIAoKCgojIyBNYXJrZG93bgoKTWFya2Rvd24gaXMgYSB2ZXJ5IHNpbXBsZSAqbWFya3VwKiBsYW5ndWFnZSB3aGljaCBwcm92aWRlcyBtZXRob2RzIGZvciBjcmVhdGluZyBkb2N1bWVudHMgd2l0aCBoZWFkZXJzLCBpbWFnZXMsIGxpbmtzIGV0Yy4gZnJvbSBwbGFpbiB0ZXh0IGZpbGVzLCB3aGlsZSBrZWVwaW5nIHRoZSBvcmlnaW5hbCBwbGFpbiB0ZXh0IGZpbGUgZWFzeSB0byByZWFkLiAKCllvdSBjYW4gY29udmVydCBNYXJrZG93biBkb2N1bWVudHMgdG8gbWFueSBvdGhlciBmaWxlIHR5cGVzIGxpa2UgYC5odG1sYCBvciBgLnBkZmAgdG8gZGlzcGxheSB0aGUgaGVhZGVycywgaW1hZ2VzIGV0Yy4uCgpJdCBtaWdodCBzb3VuZCBjb21wbGljYXRlZC4gQnV0ICpyZWFsbHkqIGlzbid0LCAKCiFbXShodHRwczovL21lZGlhLmdpcGh5LmNvbS9tZWRpYS8yNkJSTm9RSjViUmNaUzhIbS9naXBoeS5naWYpCgpGaXJzdCB0aGluZ3MgZmlyc3Q6IGluc3RhbGwgdGhlIHJlcXVpcmVkIHNvZnR3YXJlCgotIFIgYW5kIFJTdHVkaW8gKGd1ZXNzIHlvdSBoYXZlIGl0IGFscmVhZHkpCgpgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJybWFya2Rvd24iKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHJtYXJrZG93bikKYGBgCgotIGBrbml0cmAgY29tZXMgYWxvbmcsIGBwYW5kb2NgIHRvby4gWW91IHNob3VsZCBxdWlja2x5IGJlIGFsbCBzZXQhCgojIyBCYXNpY3Mgb2YgbWFya2Rvd24KCkFCQyBoZXJlLCBsZXQncyBnbyB0aHJvdWdoIGl0OgoKaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9hdXRob3JpbmdfYmFzaWNzLmh0bWwKCnBsdXMuLi4gYSBiZWF1dGlmdWwgY2hlYXQgc2hlZXQgaXMgdGhlcmUgZm9yIHlvdSEKCmh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTE1Lmh0bWwKCmh0dHA6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDMvcm1hcmtkb3duLWNoZWF0c2hlZXQtMi4wLnBkZgoKVXNpbmcgTGFUZVg/IE5vIHByb2JsZW0sIHlvdSBjYW4gdXNlICRcTGFUZVgkIGhlcmUgYXMgd2VsbCEKCiRcbGVmdCggZih4KSA9IFxzdW1fe2k9MH1ee259IFxmcmFje2FfaX17MSt4fSBccmlnaHQpJAoKV2hhdCBjYW4geW91IGRvIHdpdGggUiBtYXJrZG93bj8KCmh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZ2FsbGVyeS5odG1sCgpodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2Zvcm1hdHMuaHRtbAoKCiMjIExldCdzIGNyZWF0ZSBvbmUgdG9nZXRoZXIhCgojIyMgV2h5IGlzIFJtZCBiZXR0ZXIgdGhhbiBSCgohW10oaHR0cHM6Ly9pLmltZ2ZsaXAuY29tLzIydTU2NS5qcGcpCgpUaGUgcHJpY2UgdG8gcGF5IHRvIGhhdmUgYW4gUm1kIGRvY3VtZW50IGlzIHNvb29vbyBzbWFsbCAtIGFuZCBmb3IgdGhhdCwgeW91IGdldAoKLSBjb2RlLCB0ZXh0LCBvdXRwdXQgYWxsIHRvZ2V0aGVyCi0gb25lIGZpbGUgb25seSAtIG5vIG5lZWQgdG8gZ2V0IGxvc3QKLSBpdCBldmVuIGxvb2tzIG5pY2UgOikKCgojIyMgQ3JlYXRlIGFuIFIgTWFya2Rvd24gZmlsZSAKClRvIGNyZWF0ZSBhIG5ldyBSIE1hcmtkb3duIGZpbGUgKGAuUm1kYCksIHNlbGVjdCBgRmlsZSAtPiBOZXcgRmlsZSAtPiBSIE1hcmtkb3duLi4uYCBpbiBSU3R1ZGlvLCB0aGVuIGNob29zZSB0aGUgZmlsZSB0eXBlIHlvdSB3YW50IHRvIGNyZWF0ZS4gCgpUaGUgbmV3bHkgY3JlYXRlZCBgLlJtZGAgZmlsZSBjb21lcyB3aXRoIGJhc2ljIGluc3RydWN0aW9ucyBidXQgd2Ugd2FudCB0byBjcmVhdGUgb3VyIG93biBSIE1hcmtkb3duIHNjcmlwdCwgc28gbGV0J3MgZ2V0IHRvIGtub3cgdGhlIGRpZmZlcmVudCBwYXJ0cyBvZiBhbiBSbWQgZmlsZQoKLSBBbiAob3B0aW9uYWwpIFlBTUwgaGVhZGVyIHN1cnJvdW5kZWQgYnkgYC0tLWBzCi0gUiBjb2RlIGNodW5rcyBzdXJyb3VuZGVkIGJ5IGJhY2t0aWNrcyAoYGBgKQotIHRleHQgbWl4ZWQgd2l0aCBzaW1wbGUgdGV4dCBmb3JtYXR0aW5nCgojIyMgSW5zZXJ0aW5nIGZpZ3VyZXMgCgpVaCwgeW91IGNhbiBpbnNlcnQgZmlndXJlcyBhbHNvIGxpa2UgdGhpcwoKYGBgCiFbXShpbWFnZXMvZ3JjYXQucG5nKQpgYGAKCiFbXShpbWFnZXMvZ3JjYXQucG5nKQoKIyMgSW5zZXJ0IHRleHQgYW5kIGNvZGUgLSBhbnkgdGV4dCwgYW55IGNvZGUKCmBgYGAKYGBge3J9Cm4gPC0gMTAKcm5vcm0obikKYGBgCmBgYGAKClNob3J0Y3V0OiBgQ3RybCArIEFsdCArIElgICAgIAoKSW5wdXQgY29kZTogeW91IGNhbiB1c2UgbXVsdGlwbGUgbGFuZ3VhZ2VzIGluY2x1ZGluZyBSLCBQeXRob24sIGFuZCBTUUwsIG1hbnkgbW9yZSAoc3BlY2lmeSB0aGUgbGFuZ3VhZ2UgaW4gdGhlIGNodW5rIG9wdGlvbnMpCgoKSW5saW5lIGNvZGUgY2FuIGJlIGFkZGVkIHdpdGggYGAgYHIKMSsxYCBgYAoKCiMjIyBDaHVuayBvcHRpb25zCgpEZWF0aWxlZCB2ZXJ5IG5pY2VseSBoZXJlOiBodHRwczovL3lpaHVpLm5hbWUva25pdHIvb3B0aW9ucy8KCkEgc2ltcGxlIHNldCBvZiBvcHRpb25zIHdoaWNoIHlvdSBjYW4gdXNlIGZvciBtYW55IGRvY3VtZW50czoKCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDQyKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29tbWVudCA9IE5BLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGZpZy53aWR0aCA9IDcsCiAgZmlnLmhlaWdodCA9IDcsCiAgd2FybmluZyA9IEZBTFNFLAogIGV2YWwgPSBUUlVFCikKYGBgCgoKIyMjIEtuaXQhCgpVc2UgdGhlIGBLbml0YCBidXR0b24gaW4gdGhlIFJTdHVkaW8gSURFIHRvIHJlbmRlciB0aGUgZmlsZSBhbmQgcHJldmlldyB0aGUgb3V0cHV0IHdpdGggYSBzaW5nbGUgY2xpY2sgb3Iga2V5Ym9hcmQgc2hvcnRjdXQgKGBDdHJsICsgU2hpZnQgKyBLYCkuCgpUbyBnZW5lcmF0ZSBhIHJlcG9ydCBmcm9tIHRoZSBmaWxlLCBydW4gdGhlIGByZW5kZXJgIGNvbW1hbmQgKHdvcmtzIGFsc28gb3V0c2lkZSBvZiBSU3R1ZGlvKToKCmBgYHtyIGV2YWw9RkFMU0V9CmxpYnJhcnkoInJtYXJrZG93biIpCnJtYXJrZG93bjo6cmVuZGVyKCJ5b3VyZmlsZS5SbWQiKQpgYGAKCkl0IHdhcyBhIGRlZXAgZGl2ZSwgYnV0IG5vdy4uLgoKLSBZb3UgYXJlIGZhbWlsaWFyIHdpdGggdGhlIE1hcmtkb3duIHN5bnRheCBhbmQgY29kZSBjaHVuayBydWxlcy4KLSBZb3UgY2FuIGluY2x1ZGUgZmlndXJlcyBhbmQgdGFibGVzIGluIHlvdXIgTWFya2Rvd24gcmVwb3J0cy4KLSBZb3UgY2FuIGNyZWF0ZSBSIE1hcmtkb3duIGZpbGVzIGFuZCBleHBvcnQgdGhlbSB0byBwZGYgb3IgaHRtbCBmaWxlcy4KCgojIyAoTXVjaCkgbW9yZSBvbiBSIE1hcmtkb3duCgotIGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vCi0gaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDA3X2ZpcnN0LXVzZS1ybWFya2Rvd24uaHRtbAotIGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbAotIC4uLiB0byBodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xNS5odG1sCi0gaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9hcnRpY2xlcy5odG1sCgpZb3UgY2FuIGRvIG11Y2ggbXVjaCBtb3JlIChwcmVzZW50YXRpb25zLCB3ZWJzaXRlcywgbWFudXNjcmlwdHMsLi4uKQoKIyMgRXhlcmNpc2Ugc2Vzc2lvbiBCb251cwoKLSBjcmVhdGUgYSBuZXcgUiBNYXJrZG93biBkb2N1bWVudAotIGNhbiB5b3UgZmluZCBvdXQgaG93IHRvIGdlbmVyYXRlIGEgd29yZCBkb2N1bWVudCBhcyBvdXRwdXQ/Ci0gaW5zZXJ0IHNvbWUgY29kZSB5b3UgcHJldmlvdXNseSB1c2VkIGZvciBleHBsb3JpbmcgdGhlIHNtYWxsIHN1cnZleSBkYXRhIC0gcmVtZW1iZXIsIGEgZnJlc2ggc2Vzc2lvbiBpcyBydW4gd2hlbiBrbml0dGluZywgc28geW91IG5lZWQgdGhlIGNvbW1hbmRzIGZyb20gdGhlIHZlcnkgc3RhcnQhCgoKIyMjIEV4ZXJjaXNlIFNlc3Npb24gQm9udXMgLSBTb2x1dGlvbnMKCjxkZXRhaWxzPgo8c3VtbWFyeT5DbGljayBvbiB0aGlzIHRvIGRpc3BsYXkgdGhlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKLSBgRmlsZSAtPiBOZXcgRmlsZSAtPiBSIE1hcmtkb3duLi4uYCBpbiBSU3R1ZGlvCi0gYWRkIHRoaXMgaW4gdGhlIHlhbWwgaGVhZGVyCgpgYGAKb3V0cHV0OgogIHdvcmRfZG9jdW1lbnQKYGBgCjwhLS0tLT4KPC9kZXRhaWxzPgoKCgoKCiMgVXNlZnVsIG1hdGVyaWFsIHsjbW9yZW1hdGVyaWFsIC51bm51bWJlcmVkfQoKTWFkZSBASU1CRUkKCi0gaHR0cHM6Ly9pbWJlaW1haW56LmdpdGh1Yi5pby9HVElQSTIwMjIvIGFuZCBodHRwczovL2dpdGh1Yi5jb20vaW1iZWltYWluei9HVElQSTIwMjJfbWF0ZXJpYWxzIGlmIHlvdSB3YW50IHRvIGtub3cgbW9yZSBvbiB0aGUgcHJpbmNpcGxlcyBvZiBkYXRhIHZpc3VhbGl6YXRpb24KLSB0aGlzIHZlcnkgcmVwb3NpdG9yeSBvZiBjb3Vyc2UgbWF0ZXJpYWwgKGtlZXAgdGhpcyBtb25pdG9yZWQgb3ZlciB0aW1lISk6IGh0dHBzOi8vZ2l0aHViLmNvbS9pbWJlaW1haW56L2NvdXJzZXdhcmVfQ1RIXzIwMjQKCgpCb29rcwoKLSBSIGluIGEgbnV0c2hlbGwsIFIgY29va2Jvb2ssIFIgZ3JhcGhpY3MgY29va2Jvb2sgKEBPJ1JlaWxseSBtZWRpYSkKLSBBIEJlZ2lubmVyJ3MgR3VpZGUgdG8gUiAoWnV1ciwgSWVubywgTWVlc3RlcnMsIEBTcHJpbmdlcikKLSBSIFByb2dyYW1taW5nIGZvciBEYXRhIFNjaWVuY2UgKFBlbmcsIEBMZWFucHViKQotIFIgUHJvZ3JhbW1pbmcgZm9yIEJpb2luZm9ybWF0aWNzIChHZW50bGVtYW4sIEBDUkMpCi0gQmlvY29uZHVjdG9yIENhc2UgU3R1ZGllcyAoQFNwcmluZ2VyKQotIERhdGEgQW5hbHlzaXMgZm9yIHRoZSBMaWZlIFNjaWVuY2VzIChJcml6YXJyeSwgTG92ZSwgQExlYW5wdWIpCi0gQmlvY29uZHVjdG9yIC0gQW4gSW50cm9kdWN0aW9uIHRvIENvcmUgVGVjaG5vbG9naWVzIChIYW5zZW4sIEBMZWFucHViKQotIFIgZm9yIERhdGEgU2NpZW5jZSAoV2lja2hhbSwgR3JvbGVtdW5kLCBATydSZWlsbHkpCgotIHRoZSB3aG9sZSBgVXNlIFIhYCBib29rIHNlcmllczogaHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9ib29rc2VyaWVzLzY5OTEKLSB0aGlzIG9uZSBmcm9tIENSQzogaHR0cHM6Ly93d3cuY3JjcHJlc3MuY29tL0NoYXBtYW4tLUhhbGxDUkMtVGhlLVItU2VyaWVzL2Jvb2stc2VyaWVzL0NSQ1RIRVJTRVIKLSBodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2Jvb2svMTAuMTAwNy85NzgtMy02NjItNTM2NzAtNAotIGh0dHBzOi8vbGluay5zcHJpbmdlci5jb20vYm9vay8xMC4xMDA3Lzk3OC0zLTY2Mi00OTEwMi03CgoKQ291cnNlcwoKLSBodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY291cnNlcy9mcmVlLWludHJvZHVjdGlvbi10by1yCi0gaHR0cHM6Ly93d3cuY291cnNlcmEub3JnL3NwZWNpYWxpemF0aW9ucy9qaHUtZGF0YS1zY2llbmNlCi0gaHR0cHM6Ly93d3cuZWR4Lm9yZy9jb3Vyc2UvaW50cm9kdWN0aW9uLXItZGF0YS1zY2llbmNlLW1pY3Jvc29mdC1kYXQyMDR4LTcKCk1pc2MKCi0gaHR0cDovL3I0c3RhdHMuY29tL2FydGljbGVzL3doeS1yLWlzLWhhcmQtdG8tbGVhcm4vCi0gaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLwotIHN3aXJsIC0gbGVhcm4gUiBpbiBSOiBodHRwOi8vc3dpcmxzdGF0cy5jb20vCgo8IS0tICMjIFdoYXQgd2UgbGVmdCBvdXQgLS0+Cgo8IS0tIC0gYWR2YW5jZWQgZGF0YSBtYW5pcHVsYXRpb24gLSBgZHBseXJgIGFuZCB0aGUgbWlnaHR5IHBpcGUgLS0+CjwhLS0gLSBzdHJpbmcgbWFuaXB1bGF0aW9ucyAtLT4KPCEtLSAtIGdncGxvdDIgaW4gbW9yZSBkZXRhaWwgLS0+CjwhLS0gLSBwYWNrYWdlIGRldmVsb3BtZW50ICh5b3VyIG93biBwYWNrYWdlKSAtLT4KPCEtLSAtIHNoaW55IGFwcHMgZGV2ZWxvcG1lbnQgKGZyb20gMCB0byBhcHApIC0tPgo8IS0tIC0gcmVwcm9kdWNpYmxlIHJlcG9ydHMgKGFsc28gaW50ZXJhY3RpdmUhKSAtLT4KCgoKIyBTZXNzaW9uIEluZm8geyNzZXNzaW9uaW5mbyAudW5udW1iZXJlZH0KCkFzIGEgbGVzc29uLCBkbyB0YWtlIG5vdGUvcmVwb3J0IHRoZSB2ZXJzaW9ucyBvZiBSL3RoZSBkaWZmZXJlbnQgcGFja2FnZXMgeW91IHVzZWQgZm9yIHlvdXIgYW5hbHlzaXMuCgpXaHk/IAoKPGRldGFpbHM+CjxzdW1tYXJ5PkNsaWNrIG9uIHRoaXMgdG8gZGlzcGxheSB0aGUgc29sdXRpb248L3N1bW1hcnk+CgoqIFlvdSB3aWxsIChzaG91bGQpIHJlcG9ydCB0aGVzZSBpbiB5b3VyIE1hdGVyaWFsICYgTWV0aG9kcwoqIFlvdSBjcmVkaXQgdGhlIHdvcmsgb2Ygb3RoZXJzIC0gYXMgYSBkaXJlY3QgY29uc2VxdWVuY2UKKiBEaWZmZXJlbnQgcGFja2FnZSB2ZXJzaW9ucyBtaWdodCBsZWFkIHRvIGRpZmZlcmVudCByZXN1bHRzCiogSXQgaXMgYSBnb29kIHNjaWVudGlmaWMgcmVzZWFyY2ggcHJhY3RpY2UhCiogSXQgaGVscHMgb3RoZXJzIGhlbHAgeW91IC0gaW4gY2FzZSB5b3Ugd2FudCB0byBwb3N0IHlvdXIgcXVlc3Rpb24gb24gU3RhY2tPdmVyZmxvdyBvciBzaW1pbGFyCiogQmVjYXVzZSBvbmUgY29tbWFuZCBqdXN0IGRvZXMgaXQgZm9yIHlvdQoKPC9kZXRhaWxzPgoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==